From 4608cce8626c05844d082d1a8c6a9afac85388d8 Mon Sep 17 00:00:00 2001 From: Eric Gustin <34000337+EricGustin@users.noreply.github.com> Date: Fri, 7 Mar 2025 11:46:42 -0800 Subject: [PATCH] Fix parameterized type bug & better error message (#273) ## PR Description ### 1. Bug Fix A bug was observed where tools that were using [parameterized generics](https://docs.python.org/3/library/stdtypes.html#types-genericalias) (e.g., `dict[str, Any]`) for their input parameters or output, then this would cause the Worker to fail on startup with the error `issubclass() arg 1 must be a class` for Python3.13+. This error would not occur for Python versions less than 3.13. In Python <3.13, parameterized generics would implicitly be treated as their underlying type (its origin), so it was possible to treat `dict[str, Any]` as a class and pass it to `issubclass`. This is not the case for Python 3.13, so we need to explicitly strip the GenericAlias (e.g., `dict[str, Any]`) down to its origin (e.g., `dict`), before checking if it is a subclass of `Enum`. ### 2. Better Error Message When a tool used an unsupported parameter/output type, then Arcade would display the following message: ``` Failed to start Arcade Worker: Type error encountered while adding tool get_website_map from arcade_web.tools.firecrawl. Reason: issubclass() arg 1 must be a class ``` But now it displays ``` Failed to start Arcade Worker: Error encountered while adding tool get_website_map from arcade_web.tools.firecrawl. Reason: Unsupported type: tuple[str, str] ``` --- arcade/arcade/core/catalog.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/arcade/arcade/core/catalog.py b/arcade/arcade/core/catalog.py index e5ffbdc0..d0a1825f 100644 --- a/arcade/arcade/core/catalog.py +++ b/arcade/arcade/core/catalog.py @@ -253,6 +253,10 @@ class ToolCatalog(BaseModel): raise ToolDefinitionError( f"Type error encountered while adding tool {tool_name} from {module_name}. Reason: {e}" ) + except Exception as e: + raise ToolDefinitionError( + f"Error encountered while adding tool {tool_name} from {module_name}. Reason: {e}" + ) def __getitem__(self, name: FullyQualifiedName) -> MaterializedTool: return self.get_tool(name) @@ -623,15 +627,18 @@ def get_wire_type_info(_type: type) -> WireTypeInfo: type_to_check = inner_type if is_list else _type + # Strip generic parameters if type_to_check is a parameterized generic + actual_type = get_origin(type_to_check) or type_to_check + # Special case: Literal["string1", "string2"] can be enumerated on the wire if is_string_literal(type_to_check): is_enum = True enum_values = [str(e) for e in get_args(type_to_check)] # Special case: Enum can be enumerated on the wire - elif issubclass(type_to_check, Enum): + elif issubclass(actual_type, Enum): is_enum = True - enum_values = [e.value for e in type_to_check] # type: ignore[union-attr] + enum_values = [e.value for e in actual_type] # type: ignore[union-attr] return WireTypeInfo(wire_type, inner_wire_type, enum_values if is_enum else None) @@ -732,10 +739,10 @@ def get_wire_type( if wire_type: return wire_type - if issubclass(_type, Enum): + if isinstance(_type, type) and issubclass(_type, Enum): return "string" - if issubclass(_type, BaseModel): + if isinstance(_type, type) and issubclass(_type, BaseModel): return "json" raise ToolDefinitionError(f"Unsupported parameter type: {_type}")