#Thread
1 messages ยท Page 1 of 1 (latest)
That function isn't decorated with @function, is it?
What do your imports look like? And what's the file structure?
Did you keep the file layout from the new dagger init, or from what you had?
yes si not decorated since it is more like internal funcion, i dont really like to call it from outside. and yes i keep the file layout as from the init
Can you share more details? It's hard to tell what you have going on.
sure this si an extract of the class i have
@object_type
class Btd:
...
@function
async def run(self) -> None:
"""Run the project."""
build_promise = self.__build()
tests_promise = self.__run_tests()
[containers, tests_results] = await asyncio.gather(*[build_promise, tests_promise])
...
...
async def __build(self) -> dict[str, Container]:
"""Build project container image."""
print("Building project container images")
x = (
dag.container(platform=Platform("linux/amd64"))
.build(context=self.working_dir.directory('x'),
dockerfile="Dockerfile")
)
y = (
dag.container(platform=Platform("linux/amd64"))
.build(context=self.working_dir.directory('y'),
dockerfile="Dockerfile")
)
containers = await asyncio.gather(x, y)
print("Project container images built")
return {
"1": containers[0],
"2": containers[1]
}
...
my dagger.json
{
"name": "btd",
"engineVersion": "v0.18.4",
"sdk": {
"source": "python"
}
}
my pyproject.toml
[project]
name = "btd"
description = "Build, Test and Deploy"
version = "1.1.0"
requires-python = ">=3.12"
dependencies = [
"dagger-io>=0.18",
"requests>=2.32",
"botocore>=1.37",
"boto3>=1.37",
]
[build-system]
requires = [
"hatchling>=1.27"
]
build-backend = "hatchling.build"
[tool.uv.sources]
dagger-io = { path = "sdk", editable = true }
What's the path of the file where your class is?
It kind of looks like pyproject.toml and others are within btd/src instead of btd. Hard to say for sure from the image. Can you confirm?
no they are outside
Can you confirm the imports in __init__.py and main.py?
init.py
"""A generated module for Btd functions
This module has been generated via dagger init and serves as a reference to
basic module structure as you get started with Dagger.
Two functions have been pre-created. You can modify, delete, or add to them,
as needed. They demonstrate usage of arguments and return types using simple
echo and grep commands. The functions can be called from the dagger CLI or
from one of the SDKs.
The first line in this comment block is a short description line and the
rest is a long description with more detail on the module's purpose or usage,
if appropriate. All modules should have a short description.
"""
from .main import Btd as Btd
the main.py is the class extract i share above
But you didn't share the imports
import asyncio
import base64
import re
from typing import Annotated
import boto3
import requests
from dagger import dag, function, object_type, Directory, Doc, Container, Secret, Platform
@object_type
class Btd:
...
@function
async def run(self) -> None:
"""Run the project."""
build_promise = self.__build()
tests_promise = self.__run_tests()
[containers, tests_results] = await asyncio.gather(*[build_promise, tests_promise])
...
...
async def __build(self) -> dict[str, Container]:
"""Build project container image."""
print("Building project container images")
x = (
dag.container(platform=Platform("linux/amd64"))
.build(context=self.working_dir.directory('x'),
dockerfile="Dockerfile")
)
y = (
dag.container(platform=Platform("linux/amd64"))
.build(context=self.working_dir.directory('y'),
dockerfile="Dockerfile")
)
containers = await asyncio.gather(x, y)
print("Project container images built")
return {
"1": containers[0],
"2": containers[1]
}
...
The error you're getting... does it have a stack trace? Can you tell which line is throwing it?
let me run it on verbose mode
Does it happen with dagger functions or only dagger call?
dagger call
Doesn't happen with dagger functions?
never tried, whats the diff?
Doesn't execute any function, just shows you what the module is reporting on available functions.
Can you try it?
dagger functions
โ connect 0.3s
โ load module 1.6s
Name Description
run Run the project.
Ok, so it has to be something within the body of run. I assume you're doing dagger call run?
yes, with few arguments. but yes. and it fails when the run function calls the __build one which returns a container dict
Ok, let me see if I can make a small repro.
Ok, I was able to repro.
I suspect it's from your use of asyncio. Give me a moment pls.
great, thanks for your help
Can you tell which python version you had before?
That's the dagger version, I mean the Python version that your module was using.
we run it on GHA with
- name: Call Dagger Function
uses: dagger/dagger-for-github@8.0.0
env:
..
with:
workdir: ".github/ci/btd"
version: "latest"
args: ... run
3.12
Smallest repro here:
import asyncio
from dagger import dag, function, object_type
@object_type
class Btd:
@function
async def run(self) -> None:
x = dag.container()
y = dag.container()
containers = await asyncio.gather(x, y)
# error: Unhashable type 'Container'
containers[0]
i also test it here, same output, great we have a replicable bug. do you see something off or its something inside sdk it self?
I would do it differently, without asyncio.gather(). It's not clear to me yet how you had this working before. Nothing I can think of from the SDK side.
Even smaller repro:
@object_type
class Btd:
@function
async def run(self):
await asyncio.gather(dag.container())
And the traceback:
Traceback (most recent call last):
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 291, in get_structured_result
result = await self.call(fn.wrapped, *bound.args, **bound.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 320, in call
return await await_maybe(func(*args, **kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_utils.py", line 37, in await_maybe
return await value if inspect.iscoroutine(value) else cast(T, value)
^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/src/btd/main.py", line 11, in run
await asyncio.gather(dag.container())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/tasks.py", line 830, in gather
if arg not in arg_to_fut:
^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'Container'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 93, in serve
result = await self._invoke(parent_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 243, in _invoke
result = await self.get_result(
^^^^^^^^^^^^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 308, in get_result
result, return_type = await self.get_structured_result(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/xxh3:5997e2ba730ab363/sdk/src/dagger/mod/_module.py", line 293, in get_structured_result
raise FunctionError(e) from e
dagger.mod._exceptions.FunctionError: unhashable type: 'Container'
@oak swallow, try adding .sync() to each Container object before passing to asyncio.gather.
Like
@object_type
class Btd:
@function
async def run(self):
await self.__build()
async def __build(self) -> dict[str, dagger.Container]:
x = dag.container().sync()
y = dag.container().sync()
containers = await asyncio.gather(x, y)
return {
"1": containers[0],
"2": containers[1],
}
what will sync do?
It's not documented that you can actually do this:
await dag.container().from_("alpine")
The from_() function is lazy, but this works because the returned object (Container) implements Awaitable, making the above equivalent to:
await dag.container().from_("alpine").sync()
However, while sync() returns a coroutine, which is not exactly the same as Container implementing __await__, though it should behave the same.
Basically sync() forces evaluation of the container. Without it it's a lazy object.
The asyncio.gather function deals with a more lower level concept: "Futures". It represents an eventual result. It's better to use a task group instead, imo.
Np, still not clear why this worked for you before though ๐