The way the pulumi engine works from my experience is that Outputs don’t get resolved until the engine is running and are resolved asynchronously. So the only way to get the values is to also have an asynchronous function access the value. I still haven’t worked out how to get my own async function to operate as part of the same async loop as the engine.
The “apply” function basically formats an output as yet another output. You can’t get the “value” of that output though to use in a general Python function, it has to be part of the async chain.