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.