Skip to content
Advertisement

cx_Oracle connection type hinting in python

I’m diving into type hints and type hinting some of my code as I think it is a good practice. I have a function that takes both sqlalchemy and cx_Oracle connection/session objects. I was able to figure out the hinting for sqlalchemy here. Poked around some docs trying to figure it out with cx_Oracle to no avail.

My code

import pandas as pd

from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine

import cx_Oracle

# code that creates engine would go here 
...

def pullData(oracleEngine: __what_here__?, sqlEngine: Engine) -> pd.DataFrame:
     # do stuff
     return df

In general, are there any tips or tricks for type hinting with types that aren’t built-in core python data types? Ie I found this source for how to type hint text files. Also, is it a bad practice to give imported type hints an alias like so?

from sqlalchemy.engine.base import Engine as sql_Engine

def foo(sqlEngine: sql_Engine) ...

Advertisement

Answer

Yes, IMHO type annotations should be mandatory for everything that isn’t a quickly thrown together draft script. If you are interested in more details about typing options and best practices in Python, I recommend starting with PEP 484 and the typing module.


Regarding cx_Oracle, it seems it is written entirely in C, so there are no real Python signatures available for all their functions. I could not find any stub files either.

You also did not explain, where that oracleEngine argument is supposed to come from, so the best I can do is guess that you are referring to the object returned by the context manager cx_Oracle.connect, which in turn should be an instance of the cx_Oracle.Connection class. So this is what I would use to annotate that function parameter.


In general, are there any tips or tricks for type hinting with types that aren’t built-in core python data types?

Types are generally annotated with concrete classes or with generic types/type variables. Keep in mind that what you refer to as “built-in core python data types” are also nothing but classes. Everything in Python is an object.

So if you want to pass the return value of sqlalchemy.create_engine to a function, that function parameter should be annotated with the Engine class, as you already did.


… is it a bad practice to give imported type hints an alias […]?

Not at all. If aliasing objects on import improves readability or better conveys contextual meaning, I would say it is encouraged.

The only thing is that I would suggest sticking to PEP 8, unless you have very good reasons not to. (See my code example below.)

As a matter of fact, it can sometimes be helpful to explicitly use TypeAlias, if you have long (maybe even qualified) class names that you repeat multiple times in type annotations, such as pd.DataFrame.


All together, I would suggest to modify your example code like this:

from typing import TypeAlias

import pandas as pd
from cx_Oracle import Connection as OracleConn
from sqlalchemy.engine.base import Engine as SQLAEngine


DF: TypeAlias = pd.DataFrame


def pull_data(oracle_conn: OracleConn, sqla_engine: SQLAEngine) -> DF:
    # do stuff
    ...

Keep in mind that I made a few assumptions and guesses regarding the Oracle types. I also noticed that they mention very explicitly in their documentation that cx_Oracle is being replaced by python-oracledb. The latter also seems to support typing much better at first glance. So that may be worth upgrading to.

Hope this helps.

Advertisement