Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazily initialize config drivers field #1062

Merged
merged 4 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions griptape/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .anthropic_driver_config import AnthropicDriverConfig
from .google_driver_config import GoogleDriverConfig
from .cohere_driver_config import CohereDriverConfig
from .logging_config import LoggingConfig
from .config import config


Expand All @@ -22,5 +23,6 @@
"AnthropicDriverConfig",
"GoogleDriverConfig",
"CohereDriverConfig",
"LoggingConfig",
"config",
]
18 changes: 13 additions & 5 deletions griptape/config/base_config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
from __future__ import annotations

from abc import ABC
from typing import TYPE_CHECKING, Optional

from attrs import define
from attrs import define, field

from griptape.mixins.serializable_mixin import SerializableMixin

from .base_driver_config import BaseDriverConfig
from .logging_config import LoggingConfig
if TYPE_CHECKING:
from .base_driver_config import BaseDriverConfig
from .logging_config import LoggingConfig


@define(kw_only=True)
class BaseConfig(SerializableMixin, ABC):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope for this PR, but why do we have this base class? How would we a subclass other than _Config?

I only bring it up, because the reset method in here feels weird.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that it's probably redundant here (and maybe even adds some confusion), but I think I'd prefer to keep consistent with the pattern of having a base class for any inheritance hierarchy.

Who knows what the config classes may evolve into over time.

drivers: BaseDriverConfig
logging: LoggingConfig
_logging: Optional[LoggingConfig] = field(alias="logging")
_drivers: Optional[BaseDriverConfig] = field(alias="drivers")

def reset(self) -> None:
self._logging = None
self._drivers = None
37 changes: 32 additions & 5 deletions griptape/config/config.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
from attrs import Factory, define, field
from __future__ import annotations

from typing import TYPE_CHECKING, Optional

from attrs import define, field

from .base_config import BaseConfig
from .base_driver_config import BaseDriverConfig
from .logging_config import LoggingConfig
from .openai_driver_config import OpenAiDriverConfig

if TYPE_CHECKING:
from .base_driver_config import BaseDriverConfig

@define

@define(kw_only=True)
class _Config(BaseConfig):
drivers: BaseDriverConfig = field(default=Factory(lambda: OpenAiDriverConfig()), kw_only=True)
logging: LoggingConfig = field(default=Factory(lambda: LoggingConfig()), kw_only=True)
_logging: Optional[LoggingConfig] = field(default=None, alias="logging")
_drivers: Optional[BaseDriverConfig] = field(default=None, alias="drivers")

@property
def drivers(self) -> BaseDriverConfig:
"""Lazily instantiates the drivers configuration to avoid client errors like missing API key."""
if self._drivers is None:
self._drivers = OpenAiDriverConfig()
return self._drivers

@drivers.setter
def drivers(self, drivers: BaseDriverConfig) -> None:
self._drivers = drivers

@property
def logging(self) -> LoggingConfig:
if self._logging is None:
self._logging = LoggingConfig()
return self._logging

@logging.setter
def logging(self, logging: LoggingConfig) -> None:
self._logging = logging

Check warning on line 39 in griptape/config/config.py

View check run for this annotation

Codecov / codecov/patch

griptape/config/config.py#L39

Added line #L39 was not covered by tests


config = _Config()
25 changes: 25 additions & 0 deletions tests/unit/config/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from griptape.config.openai_driver_config import OpenAiDriverConfig


class TestConfig:
@pytest.mark.skip_mock_config()
def test_init(self):
from griptape.config import LoggingConfig, config

assert isinstance(config.drivers, OpenAiDriverConfig)
assert isinstance(config.logging, LoggingConfig)

@pytest.mark.skip_mock_config()
def test_lazy_init(self):
from griptape.config import config

assert config._drivers is None
assert config._logging is None

assert config.drivers is not None
assert config.logging is not None

assert config._drivers is not None
assert config._logging is not None
20 changes: 16 additions & 4 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import pytest

from griptape.config import config
from griptape.events import event_bus
from tests.mocks.mock_driver_config import MockDriverConfig


@pytest.fixture(autouse=True)
def mock_event_bus():
from griptape.events import event_bus

event_bus.clear_event_listeners()

yield event_bus
Expand All @@ -15,7 +15,19 @@ def mock_event_bus():


@pytest.fixture(autouse=True)
def mock_config():
def mock_config(request):
from griptape.config import config

config.reset()

# Some tests we don't want to use the autouse fixture's MockDriverConfig
if "skip_mock_config" in request.keywords:
yield

return

config.drivers = MockDriverConfig()

return config
yield config

config.reset()
Loading