I can't figure out what kind of object a sqlalchemy query returns.
entries = session.query(Foo.id, Foo.date).all()
The type of each object in entries seems to be sqlalchemy.util._collections.result
, but a quick from sqlalchemy.util._collections import result
in a python interpreter raises an ImportError.
What I'm ultimately trying to do is to type hint this function:
def my_super_function(session: Session) -> ???:
entries = session.query(Foo.id, Foo.date).all()
return entries
What should I put in place of ???
? mypy (in this case) seems to be fine with List[Tuple[int, str]]
because yes indeed I can access my entries like if they were tuples, but i can also access them with entry.date
, for example.
I too found it curious that the class couldn't be imported. The answer is pretty long as I've walked you through how I've worked it out, bear with me.
Query.all()
callslist()
on theQuery
object itself:... where list will be iterating over the object, so
Query.__iter__()
:... returns the result of
Query._execute_and_instances()
method:Which executes the query and returns the result of
sqlalchemy.loading.instances()
function. In that function there is this line which applies to non-single-entity queries:... and if I stick a
print(keyed_tuple)
in after that line it prints<class 'sqlalchemy.util._collections.result'>
, which is the type that you mention above. So whatever that object is, it's coming from thesqlalchemy.util._collections.lightweight_named_tuple()
function:So the key part is this statement:
... which calls the built in
type()
class which according to the docs:And this is why you cannot import the class
sqlalchemy.util._collections.result
- because the class is only constructed at query time. I'd say that the reason for this is that the column names (i.e. the named tuple attributes) aren't known until the query is executed).From python docs the signature for
type
is:type(name, bases, dict)
where:As you can see, the
bases
argument passed totype()
inlightweight_named_tuple()
is(_LW,)
. So any of the dynamically created named tuple types inherit fromsqlalchemy.util._collections._LW
, which is a class that you can import:... so I'm not sure whether it's good form to type your function to an internal class with the leading underscore, but
_LW
inherits fromsqlalchemy.util._collections.AbstractKeyedTuple
, which itself inherits fromtuple
. That's why your current typing ofList[Tuple[int, str]]
works, because it is a list of tuples. So take your pick,_LW
,AbstractKeyedTuple
,tuple
would all be correct representations of what your function is returning.