Gracefully handle toolkit load errors (#67)
This commit is contained in:
parent
894fa878f1
commit
5b9438da82
5 changed files with 41 additions and 12 deletions
|
|
@ -17,7 +17,9 @@ except ImportError:
|
||||||
try:
|
try:
|
||||||
import uvicorn
|
import uvicorn
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ImportError("Uvicorn is not installed. Please install it using `pip install uvicorn`.")
|
raise ImportError(
|
||||||
|
"Uvicorn is not installed. Please install it using `pip install arcade-ai[fastapi]`."
|
||||||
|
)
|
||||||
|
|
||||||
from arcade.actor.fastapi.actor import FastAPIActor
|
from arcade.actor.fastapi.actor import FastAPIActor
|
||||||
from arcade.core.toolkit import Toolkit
|
from arcade.core.toolkit import Toolkit
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from typer.models import Context
|
||||||
|
|
||||||
from arcade.core.catalog import ToolCatalog
|
from arcade.core.catalog import ToolCatalog
|
||||||
from arcade.core.config_model import Config
|
from arcade.core.config_model import Config
|
||||||
|
from arcade.core.errors import ToolkitLoadError
|
||||||
from arcade.core.toolkit import Toolkit
|
from arcade.core.toolkit import Toolkit
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -34,10 +35,10 @@ def create_cli_catalog(
|
||||||
try:
|
try:
|
||||||
prefixed_toolkit = "arcade_" + toolkit
|
prefixed_toolkit = "arcade_" + toolkit
|
||||||
toolkits = [Toolkit.from_package(prefixed_toolkit)]
|
toolkits = [Toolkit.from_package(prefixed_toolkit)]
|
||||||
except ValueError:
|
except ToolkitLoadError:
|
||||||
try: # try without prefix
|
try: # try without prefix
|
||||||
toolkits = [Toolkit.from_package(toolkit)]
|
toolkits = [Toolkit.from_package(toolkit)]
|
||||||
except ValueError as e:
|
except ToolkitLoadError as e:
|
||||||
console.print(f"❌ {e}", style="bold red")
|
console.print(f"❌ {e}", style="bold red")
|
||||||
typer.Exit(code=1)
|
typer.Exit(code=1)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,22 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ToolkitError(Exception):
|
||||||
|
"""
|
||||||
|
Base class for all errors related to toolkits.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ToolkitLoadError(ToolkitError):
|
||||||
|
"""
|
||||||
|
Raised when there is an error loading a toolkit.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ToolError(Exception):
|
class ToolError(Exception):
|
||||||
"""
|
"""
|
||||||
Base class for all errors related to tools.
|
Base class for all errors related to tools.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import types
|
import types
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
@ -7,8 +8,11 @@ from pathlib import Path
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, field_validator
|
from pydantic import BaseModel, ConfigDict, field_validator
|
||||||
|
|
||||||
|
from arcade.core.errors import ToolkitLoadError
|
||||||
from arcade.core.parse import get_tools_from_file
|
from arcade.core.parse import get_tools_from_file
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Toolkit(BaseModel):
|
class Toolkit(BaseModel):
|
||||||
model_config = ConfigDict(populate_by_name=True)
|
model_config = ConfigDict(populate_by_name=True)
|
||||||
|
|
@ -64,23 +68,23 @@ class Toolkit(BaseModel):
|
||||||
repo = metadata.get("Repository", None) # type: ignore[attr-defined]
|
repo = metadata.get("Repository", None) # type: ignore[attr-defined]
|
||||||
|
|
||||||
except importlib.metadata.PackageNotFoundError as e:
|
except importlib.metadata.PackageNotFoundError as e:
|
||||||
raise ValueError(f"Package {package} not found.") from e
|
raise ToolkitLoadError(f"Package {package} not found.") from e
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise ValueError(f"Metadata key error for package {package}.") from e
|
raise ToolkitLoadError(f"Metadata key error for package {package}.") from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"Failed to load metadata for package {package}.") from e
|
raise ToolkitLoadError(f"Failed to load metadata for package {package}.") from e
|
||||||
|
|
||||||
# Get the package directory
|
# Get the package directory
|
||||||
try:
|
try:
|
||||||
package_dir = Path(get_package_directory(package))
|
package_dir = Path(get_package_directory(package))
|
||||||
except AttributeError as e:
|
except (ImportError, AttributeError) as e:
|
||||||
raise ValueError(f"Failed to locate package directory for {package}.") from e
|
raise ToolkitLoadError(f"Failed to locate package directory for {package}.") from e
|
||||||
|
|
||||||
# Get all python files in the package directory
|
# Get all python files in the package directory
|
||||||
try:
|
try:
|
||||||
modules = [f for f in package_dir.glob("**/*.py") if f.is_file()]
|
modules = [f for f in package_dir.glob("**/*.py") if f.is_file()]
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise ValueError(
|
raise ToolkitLoadError(
|
||||||
f"Failed to locate Python files in package directory for {package}."
|
f"Failed to locate Python files in package directory for {package}."
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
@ -101,7 +105,7 @@ class Toolkit(BaseModel):
|
||||||
toolkit.tools[import_path] = get_tools_from_file(str(module_path))
|
toolkit.tools[import_path] = get_tools_from_file(str(module_path))
|
||||||
|
|
||||||
if not toolkit.tools:
|
if not toolkit.tools:
|
||||||
raise ValueError(f"No tools found in package {package}")
|
raise ToolkitLoadError(f"No tools found in package {package}")
|
||||||
|
|
||||||
return toolkit
|
return toolkit
|
||||||
|
|
||||||
|
|
@ -123,7 +127,13 @@ class Toolkit(BaseModel):
|
||||||
for dist in importlib.metadata.distributions(path=[site_packages_dir])
|
for dist in importlib.metadata.distributions(path=[site_packages_dir])
|
||||||
if dist.metadata["Name"].startswith("arcade_")
|
if dist.metadata["Name"].startswith("arcade_")
|
||||||
]
|
]
|
||||||
return [cls.from_package(package) for package in arcade_packages]
|
toolkits = []
|
||||||
|
for package in arcade_packages:
|
||||||
|
try:
|
||||||
|
toolkits.append(cls.from_package(package))
|
||||||
|
except ToolkitLoadError as e:
|
||||||
|
logger.warning(f"Warning: {e} Skipping toolkit {package}")
|
||||||
|
return toolkits
|
||||||
|
|
||||||
|
|
||||||
def get_package_directory(package_name: str) -> str:
|
def get_package_directory(package_name: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import datetime
|
||||||
import re
|
import re
|
||||||
from base64 import urlsafe_b64decode
|
from base64 import urlsafe_b64decode
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Optional, dict
|
from typing import Any, Optional
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue