This message was deleted.
# hamilton-help
s
This message was deleted.
e
Hey! Easiest way is to define another function that creates it:
Copy code
def session() -> Session:
    ... # load it up -- feel free to declare parameters as inputs
Then you can include these in a
resources
module which you’d add to your driver. The cool thing is that, if you want to read from another db source, you could always swap out a
prod_resources
with.a
dev_resources
module in the driver! Does this help? There are other ways to achieve what you want as well, but this is a pattern we’ve found that people really like. `Then you often include these in a
m
Thanks it works! Then I have another question. I want to handle a dependency via a context manager like:
Copy code
import contextlib
import typing

import requests


@contextlib.contextmanager
def session() -> typing.Generator[requests.Session, None, None]:
    with requests.Session() as session:
        yield session


async def a(input: pd.Series, session: requests.Session) -> pd.Series:
    ...
To close a session gracefully. But the code throws an error
ValueError: Error: a is expecting session:<class 'requests.sessions.Session'>, but found session:typing.Generator[requests.sessions.Session, NoneType, NoneType]
. Is there a workaround?
e
So yeah, it doesn’t work with generators, although this is a feature we should think about. the way it works is just returning it. There are a few approaches to handle this though: 1. You can request it as a parameter and call close on it later (out of band). You can hide this with a tag — e.g. tag all the nodes with 2. You can not worry about it — it should ideally handle it when garbage collecting the session object (although you’d have to dig in more to ensure) 3. you can pass it in as an input to the driver — this means not using the resource module but having the context manager instead You could also write a hook to close all the outputs afterwards, but that’s probably too advanced.
That said, the best approach depends on the context you’re running in — whats running the code? what’s the lifespan of the session? How will it be used/will it be reused?
m
Thanks and let me check your answers. I really appreciate your help. I’m trying to use Hamilton along with FastAPI. A dependency will be injected via
Depends
in FastAPI and a dependency is not defined in a lifespan.
e
Ok, so just to be clear, is the session defined in the
dependency
in
FastAPI
?
m
Yes I have a FastAPI app and it uses many dependencies. https://fastapi.tiangolo.com/tutorial/dependencies/ So I want to reuse it in Hamilton. But I think it’s impossible to passing a FastAPI dependency to a function in a Hamilton module. So I asked the first question. Also this is a reason why I asked a question about using context-manager wrapped function/generator. It’s common to use a generator in FastAPI dependency.
e
Interesting — so just to be clear are you running Hamilton inside fastAPI? Or using the same dependency functions for a Hamilton and FastAPI context? More broadly, what’s the desired control flow/structure?
m
My FastAPI app gets an input and creates a dataframe based on it. What I think the most desirable solution for my case case is making possible to pass an argument (dependency) to a Hamilton function. But I think it’s difficult. Then I want to reuse dependency functions/generators for FastAPI in Hamilton.
e
Ahh yep, makes sense. To be clear, does something like this work?
Copy code
from hamilton.plugins import h_async 

async def common_parameters(...) -> 

async_driver = ...h_async.AsyncDriver({}, modules) # async driver in async func.

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    result = await async_driver.execute([...], inputs={"session": commons["session"]})
    # can close/do whatever with session
    return result
E.G. just rely on fastAPI to compute the dependencies and pass them in as an input to Hamilton?
m
Oh, now I know I can inject something via
inputs
. It's my bad and now I'm feel I'm very stupid.
e
Heh absolutely not — its my fault I jumped to all the power-user cases 😆
👍 1
t
Hey! Thanks for sharing that question, I'm certain it can valuable to others too! Depending on the code portability you want, an approach is to have your
driver.execute()
within the scope of the context manager and pass the session as input. Something like this:
Copy code
# my_functions.py
def a(session: requests.Session) -> int: ...

# run.py
@contextlib.contextmanager
def session() -> typing.Generator[requests.Session, None, None]:
    with requests.Session() as session:
        yield session

dr = driver.Builder().with_modules(my_functions).build()

with session(...) as my_session:
     results = dr.execute(["a"], inputs=dict(session=my_session))

print(results)
We have more info about Hamilton + FastAPI in the docs FastAPI has advanced features for context managers and database connections , using
Depends
, but it might be overkill as a first step
m
Thanks and sorry I overlooked that document & also I don’t know why but I never thought I can pass non data (non dataframe data) in “inputs”. It’s my bad.
t
No issue really! Happy to help 🙂
👍 1