Ryan Whitten
07/11/2024, 5:22 PMresult_builder
. With the regular Builder I would normally set it using with_adapters()
, e.g.:
result_builder = base.PandasDataFrameResult()
await AsyncBuilder().with_config(self.config).with_modules(*self.included_modules).with_adapters(result_builder).build()
but this raises ValueError("You cannot pass more than one result builder to the async driver. Please pass in a single result builder")
here: https://github.com/DAGWorks-Inc/hamilton/blob/main/hamilton/async_driver.py#L225-L229
Looks like that is due to a default DictResult getting set if the AsyncBuilder.result_builder
is None: https://github.com/DAGWorks-Inc/hamilton/blob/main/hamilton/async_driver.py#L478-L480
I'm able to work around it by setting the builder's result_builder
attr before calling build()
, but not sure if that is the intended usage:
result_builder = base.PandasDataFrameResult()
builder = AsyncBuilder().with_config(self.config).with_modules(*self.included_modules)
builder.result_builder = result_builder
await builder.build()
Elijah Ben Izzy
07/11/2024, 6:15 PMRyan Whitten
07/11/2024, 6:19 PMAsyncBuilder.build
is async, it means I'd have to break my encapsulation and have parent async components (e.g. fastapi app) call down through to build the driver asynchronously. Would it be possible to add a non-async version of build
? Or would that not play well with potential async lifecycle adapters? Alternative is maybe I manually build AsyncDriver
without the builder for nowElijah Ben Izzy
07/11/2024, 6:29 PM.ainit()
later.
My standard approach is to add “shadow methods” (E.G. those that start with a
) to your call chain, rather than messing around with asyncio.run
(which is possible but risky). There are trade-offs (it often involves some duplicated code), but it’s kept me pretty sane. Then I’ll instantiate it in the lifespan method: https://github.com/DAGWorks-Inc/hamilton/blob/main/examples/async/fastapi_example.py#L33.
If that’s not feasible, then here are some workarounds, but I think it would be reasonable to have a non-init power-user method. Can scope that out shortly, and see if I like it/add to the next minor release. So yeah, for now I think your solution is OK for now but here are some ideas:
Some workarounds:
1. Instantiate it directly without calling .ainit
(probably a good approach for now)
2. Use asynio.run() (nasty, TBH, but it can work sometimes)
3. Subclass with a non_init_build()
function that just does everything in these functions (also not ideal)Ryan Whitten
07/11/2024, 6:35 PMElijah Ben Izzy
07/11/2024, 6:36 PMRyan Whitten
07/11/2024, 6:43 PMElijah Ben Izzy
07/11/2024, 6:49 PMElijah Ben Izzy
07/11/2024, 7:49 PMElijah Ben Izzy
07/11/2024, 7:49 PMRyan Whitten
07/11/2024, 7:51 PMElijah Ben Izzy
07/11/2024, 7:52 PMElijah Ben Izzy
07/11/2024, 8:56 PMsf-hamilton==1.71.0rc0
a try? Happened to have a few minutes to hammer it out.
The new function is build_without_init
which gives you a non-initialized async driver. Up to you to initialize (recommend it), even though you might not be using those that are required to get initialized. Should have fixes for the other issues as well!Ryan Whitten
07/11/2024, 9:05 PMElijah Ben Izzy
07/11/2024, 9:05 PMRyan Whitten
07/11/2024, 9:07 PMElijah Ben Izzy
07/11/2024, 9:08 PM