diff --git a/sdk/python/src/dagger/api/gen.py b/sdk/python/src/dagger/api/gen.py index 94aea46864d..3d9606004bb 100644 --- a/sdk/python/src/dagger/api/gen.py +++ b/sdk/python/src/dagger/api/gen.py @@ -427,14 +427,14 @@ def with_exec( _ctx = self._select("withExec", _args) return Container(_ctx) - def with_fs(self, id: "DirectoryID | Directory") -> "Container": + def with_fs(self, id: "Directory") -> "Container": """Initialize this container from this DirectoryID .. deprecated:: Replaced by :py:meth:`with_rootfs`. """ _args = [ - Arg("id", "id", id, DirectoryID | Directory), + Arg("id", "id", id, Directory), ] _ctx = self._select("withFS", _args) return Container(_ctx) @@ -506,10 +506,10 @@ def with_new_file(self, path: str, contents: str | None = None) -> "Container": _ctx = self._select("withNewFile", _args) return Container(_ctx) - def with_rootfs(self, id: "DirectoryID | Directory") -> "Container": + def with_rootfs(self, id: "Directory") -> "Container": """Initialize this container from this DirectoryID""" _args = [ - Arg("id", "id", id, DirectoryID | Directory), + Arg("id", "id", id, Directory), ] _ctx = self._select("withRootfs", _args) return Container(_ctx) @@ -1100,9 +1100,7 @@ def cache_volume(self, key: str) -> "CacheVolume": return CacheVolume(_ctx) def container( - self, - id: "ContainerID | Container | None" = None, - platform: "Platform | None" = None, + self, id: "ContainerID | None" = None, platform: "Platform | None" = None ) -> "Container": """Load a container from ID. @@ -1113,7 +1111,7 @@ def container( host. """ _args = [ - Arg("id", "id", id, ContainerID | Container | None, None), + Arg("id", "id", id, ContainerID | None, None), Arg("platform", "platform", platform, Platform | None, None), ] _ctx = self._select("container", _args) @@ -1125,18 +1123,18 @@ async def default_platform(self) -> Platform: _ctx = self._select("defaultPlatform", _args) return await _ctx.execute(Platform) - def directory(self, id: "DirectoryID | Directory | None" = None) -> "Directory": + def directory(self, id: "DirectoryID | None" = None) -> "Directory": """Load a directory by ID. No argument produces an empty directory.""" _args = [ - Arg("id", "id", id, DirectoryID | Directory | None, None), + Arg("id", "id", id, DirectoryID | None, None), ] _ctx = self._select("directory", _args) return Directory(_ctx) - def file(self, id: "FileID | File") -> "File": + def file(self, id: "FileID") -> "File": """Load a file by ID""" _args = [ - Arg("id", "id", id, FileID | File), + Arg("id", "id", id, FileID), ] _ctx = self._select("file", _args) return File(_ctx) @@ -1172,18 +1170,18 @@ def project(self, name: str) -> "Project": _ctx = self._select("project", _args) return Project(_ctx) - def secret(self, id: "SecretID | Secret") -> "Secret": + def secret(self, id: "SecretID") -> "Secret": """Load a secret from its ID""" _args = [ - Arg("id", "id", id, SecretID | Secret), + Arg("id", "id", id, SecretID), ] _ctx = self._select("secret", _args) return Secret(_ctx) - def socket(self, id: "SocketID | Socket | None" = None) -> "Socket": + def socket(self, id: "SocketID | None" = None) -> "Socket": """Load a socket by ID""" _args = [ - Arg("id", "id", id, SocketID | Socket | None, None), + Arg("id", "id", id, SocketID | None, None), ] _ctx = self._select("socket", _args) return Socket(_ctx) diff --git a/sdk/python/src/dagger/api/gen_sync.py b/sdk/python/src/dagger/api/gen_sync.py index bdd144c1f79..28c07cf24c0 100644 --- a/sdk/python/src/dagger/api/gen_sync.py +++ b/sdk/python/src/dagger/api/gen_sync.py @@ -427,14 +427,14 @@ def with_exec( _ctx = self._select("withExec", _args) return Container(_ctx) - def with_fs(self, id: "DirectoryID | Directory") -> "Container": + def with_fs(self, id: "Directory") -> "Container": """Initialize this container from this DirectoryID .. deprecated:: Replaced by :py:meth:`with_rootfs`. """ _args = [ - Arg("id", "id", id, DirectoryID | Directory), + Arg("id", "id", id, Directory), ] _ctx = self._select("withFS", _args) return Container(_ctx) @@ -506,10 +506,10 @@ def with_new_file(self, path: str, contents: str | None = None) -> "Container": _ctx = self._select("withNewFile", _args) return Container(_ctx) - def with_rootfs(self, id: "DirectoryID | Directory") -> "Container": + def with_rootfs(self, id: "Directory") -> "Container": """Initialize this container from this DirectoryID""" _args = [ - Arg("id", "id", id, DirectoryID | Directory), + Arg("id", "id", id, Directory), ] _ctx = self._select("withRootfs", _args) return Container(_ctx) @@ -1100,9 +1100,7 @@ def cache_volume(self, key: str) -> "CacheVolume": return CacheVolume(_ctx) def container( - self, - id: "ContainerID | Container | None" = None, - platform: "Platform | None" = None, + self, id: "ContainerID | None" = None, platform: "Platform | None" = None ) -> "Container": """Load a container from ID. @@ -1113,7 +1111,7 @@ def container( host. """ _args = [ - Arg("id", "id", id, ContainerID | Container | None, None), + Arg("id", "id", id, ContainerID | None, None), Arg("platform", "platform", platform, Platform | None, None), ] _ctx = self._select("container", _args) @@ -1125,18 +1123,18 @@ def default_platform(self) -> Platform: _ctx = self._select("defaultPlatform", _args) return _ctx.execute_sync(Platform) - def directory(self, id: "DirectoryID | Directory | None" = None) -> "Directory": + def directory(self, id: "DirectoryID | None" = None) -> "Directory": """Load a directory by ID. No argument produces an empty directory.""" _args = [ - Arg("id", "id", id, DirectoryID | Directory | None, None), + Arg("id", "id", id, DirectoryID | None, None), ] _ctx = self._select("directory", _args) return Directory(_ctx) - def file(self, id: "FileID | File") -> "File": + def file(self, id: "FileID") -> "File": """Load a file by ID""" _args = [ - Arg("id", "id", id, FileID | File), + Arg("id", "id", id, FileID), ] _ctx = self._select("file", _args) return File(_ctx) @@ -1172,18 +1170,18 @@ def project(self, name: str) -> "Project": _ctx = self._select("project", _args) return Project(_ctx) - def secret(self, id: "SecretID | Secret") -> "Secret": + def secret(self, id: "SecretID") -> "Secret": """Load a secret from its ID""" _args = [ - Arg("id", "id", id, SecretID | Secret), + Arg("id", "id", id, SecretID), ] _ctx = self._select("secret", _args) return Secret(_ctx) - def socket(self, id: "SocketID | Socket | None" = None) -> "Socket": + def socket(self, id: "SocketID | None" = None) -> "Socket": """Load a socket by ID""" _args = [ - Arg("id", "id", id, SocketID | Socket | None, None), + Arg("id", "id", id, SocketID | None, None), ] _ctx = self._select("socket", _args) return Socket(_ctx) diff --git a/sdk/python/src/dagger/codegen.py b/sdk/python/src/dagger/codegen.py index d130a9bba76..cf5dcdacc1a 100644 --- a/sdk/python/src/dagger/codegen.py +++ b/sdk/python/src/dagger/codegen.py @@ -10,7 +10,16 @@ from itertools import chain, groupby from keyword import iskeyword from operator import attrgetter -from typing import Any, ClassVar, Generic, Iterator, Protocol, TypeGuard, TypeVar +from typing import ( + Any, + ClassVar, + Generic, + Iterator, + Protocol, + TypeAlias, + TypeGuard, + TypeVar, +) from attrs import Factory, define from graphql import ( @@ -48,6 +57,11 @@ wrap_indent = partial(wrap, initial_indent=" " * 4, subsequent_indent=" " * 4) +IDName: TypeAlias = str +TypeName: TypeAlias = str +IDMap: TypeAlias = dict[IDName, TypeName] + + class Scalars(Enum): ID = str Int = int @@ -94,7 +108,7 @@ def generate(schema: GraphQLSchema, sync: bool = False) -> Iterator[str]: # collect object types for all id return types # used to replace custom scalars by objects in inputs - id_map: dict[str, str] = {} + id_map: IDMap = {} for type_name, t in schema.type_map.items(): if is_wrapping_type(t): t = t.of_type @@ -189,7 +203,7 @@ def format_name(s: str) -> str: return s -def format_input_type(t: GraphQLInputType, id_map: dict[str, str]) -> str: +def format_input_type(t: GraphQLInputType, id_map: IDMap) -> str: """This may be used in an input object field or an object field parameter.""" if is_required_type(t): @@ -245,17 +259,30 @@ def __init__( self, name: str, graphql: GraphQLInputField | GraphQLArgument, - id_map: dict[str, str], + id_map: IDMap, + parent: "_ObjectField | None" = None, ) -> None: self.graphql_name = name self.graphql = graphql self.name = format_name(name) + named_type = get_named_type(graphql.type) + + # On object type fields, don't replace ID scalar with object + # only if field name is `id` and the corresponding type is different + # from the output type (e.g., `file(id: FileID) -> File`, but also + # `with_rootfs(id: Directory) -> Container`). + if ( + name == "id" + and is_custom_scalar_type(graphql.type) + and named_type.name in id_map + and parent + and get_named_type(parent.graphql.type).name == id_map[named_type.name] + ): + id_map = {} + self.type = format_input_type(graphql.type, id_map) - if name == "id" and is_custom_scalar_type(graphql.type): - self.type = f"{get_named_type(graphql.type)} | {self.type}" self.description = graphql.description - self.has_default = graphql.default_value is not Undefined self.default_value = graphql.default_value @@ -301,7 +328,7 @@ def __init__( self, name: str, field: GraphQLField, - id_map: dict[str, str], + id_map: IDMap, sync: bool, ) -> None: self.graphql_name = name @@ -310,7 +337,7 @@ def __init__( self.name = format_name(name) self.args = sorted( - (_InputField(*args, id_map) for args in field.args.items()), + (_InputField(*args, id_map, parent=self) for args in field.args.items()), key=attrgetter("has_default"), ) self.description = field.description @@ -422,7 +449,7 @@ class Handler(ABC, Generic[_H]): sync: bool = False """Sync or async.""" - id_map: dict[str, str] = Factory(dict) + id_map: IDMap = Factory(dict) """Map to convert ids (custom scalars) to corresponding types.""" predicate: ClassVar[Predicate] = staticmethod(lambda _: True)