Skip to content
This repository has been archived by the owner on Sep 12, 2023. It is now read-only.

Commit

Permalink
refactor: default_factory and validator for dependency-based gates
Browse files Browse the repository at this point in the history
Default values for `PluginConfig.do_cache` et al. are based on the
`IS_XXX_INSTALLED` constants.

We use `default_factory` so we can patch the module values in tests.

Use `@validator` to test for missing dependency when gates are
explicitly set.
  • Loading branch information
peterschutt committed Jan 18, 2023
1 parent 681ae16 commit 4ad9cb3
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/starlite_saqlalchemy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AuthorizationError(StarliteSaqlalchemyClientError):
"""A user tried to do something they shouldn't have."""


class MissingDependencyError(StarliteSaqlalchemyError):
class MissingDependencyError(StarliteSaqlalchemyError, ValueError):
"""A required dependency is not installed."""

def __init__(self, module: str, config: str | None = None) -> None:
Expand Down
54 changes: 35 additions & 19 deletions src/starlite_saqlalchemy/init_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def example_handler() -> dict:
from collections.abc import Callable, Sequence # noqa: TC003
from typing import TYPE_CHECKING, Any, TypeVar

from pydantic import BaseModel
from pydantic import BaseModel, Field, validator
from starlite.app import DEFAULT_CACHE_CONFIG, DEFAULT_OPENAPI_CONFIG
from starlite.types import TypeEncodersMap # noqa: TC002
from structlog.types import Processor # noqa: TC002
Expand Down Expand Up @@ -92,7 +92,7 @@ class PluginConfig(BaseModel):
Add the hook handler to
[`AppConfig.after_exception`][starlite.config.app.AppConfig.after_exception].
"""
do_cache: bool = False
do_cache: bool = Field(default_factory=lambda: IS_REDIS_INSTALLED)
"""Configure redis cache backend.
Add configuration for the redis-backed cache to
Expand Down Expand Up @@ -134,7 +134,10 @@ class PluginConfig(BaseModel):
Set the OpenAPI config object to
[`AppConfig.openapi_config`][starlite.config.app.AppConfig.openapi_config].
"""
do_sentry: bool | None = None
do_sentry: bool = Field(
default_factory=lambda: IS_SENTRY_SDK_INSTALLED
and not (IS_LOCAL_ENVIRONMENT or IS_TEST_ENVIRONMENT)
)
"""Configure sentry.
Configure the application to initialize Sentry on startup. Adds a handler to
Expand All @@ -146,7 +149,7 @@ class PluginConfig(BaseModel):
Allow the plugin to set the starlite `debug` parameter. Parameter set to value of
[`AppConfig.debug`][starlite_saqlalchemy.settings.AppSettings.DEBUG].
"""
do_sqlalchemy_plugin: bool = False
do_sqlalchemy_plugin: bool = Field(default_factory=lambda: IS_SQLALCHEMY_INSTALLED)
"""Configure SQLAlchemy plugin.
Set the SQLAlchemy plugin on the application. Adds the plugin to
Expand All @@ -155,7 +158,7 @@ class PluginConfig(BaseModel):
do_type_encoders: bool = True
"""Configure custom type encoders on the app."""

do_worker: bool = False
do_worker: bool = Field(default_factory=lambda: IS_SAQ_INSTALLED)
"""Configure the async worker on the application.
This action instantiates a worker instance and sets handlers for
Expand All @@ -170,6 +173,31 @@ class PluginConfig(BaseModel):
type_encoders: TypeEncodersMap = type_encoders_map
"""Map of type to serializer callable."""
health_checks: list[type[AbstractHealthCheck]] = [AppHealthCheck]
"""Checks executed on calls to health route handler."""

@validator("do_cache")
def _validate_do_cache(cls, value: bool) -> bool:
if value is True and not IS_REDIS_INSTALLED:
raise MissingDependencyError(module="redis", config="redis")
return value

@validator("do_sentry")
def _validate_do_sentry(cls, value: bool) -> bool:
if value is True and not IS_SENTRY_SDK_INSTALLED:
raise MissingDependencyError(module="sentry_sdk", config="sentry")
return value

@validator("do_sqlalchemy_plugin")
def _validate_do_sqlalchemy_plugin(cls, value: bool) -> bool:
if value is True and not IS_SQLALCHEMY_INSTALLED:
raise MissingDependencyError(module="sqlalchemy", config="sqlalchemy_plugin")
return value

@validator("do_worker")
def _validate_do_worker(cls, value: bool) -> bool:
if value is True and not IS_SAQ_INSTALLED:
raise MissingDependencyError(module="saq", config="worker")
return value


class ConfigureApp:
Expand Down Expand Up @@ -207,6 +235,7 @@ def __call__(self, app_config: AppConfig) -> AppConfig:
self.configure_sqlalchemy_plugin(app_config)
self.configure_type_encoders(app_config)
self.configure_worker(app_config)
# health check is explicitly configured last
self.configure_health_check(app_config)

app_config.before_startup = lifespan.before_startup_handler
Expand Down Expand Up @@ -237,8 +266,6 @@ def configure_cache(self, app_config: AppConfig) -> None:
app_config: The Starlite application config object.
"""
if self.config.do_cache and app_config.cache_config == DEFAULT_CACHE_CONFIG:
if not IS_REDIS_INSTALLED:
raise MissingDependencyError(module="redis", config="redis")
from starlite_saqlalchemy import cache

app_config.cache_config = cache.config
Expand Down Expand Up @@ -340,14 +367,7 @@ def configure_sentry(self, app_config: AppConfig) -> None:
Args:
app_config: The Starlite application config object.
"""
do_sentry = (
self.config.do_sentry
if self.config.do_sentry is not None
else not (IS_LOCAL_ENVIRONMENT or IS_TEST_ENVIRONMENT)
)
if do_sentry:
if not IS_SENTRY_SDK_INSTALLED:
raise MissingDependencyError(module="sentry_sdk", config="sentry")
if self.config.do_sentry:
from starlite_saqlalchemy import sentry

app_config.on_startup.append(sentry.configure)
Expand All @@ -362,8 +382,6 @@ def configure_sqlalchemy_plugin(self, app_config: AppConfig) -> None:
app_config: The Starlite application config object.
"""
if self.config.do_sqlalchemy_plugin:
if not IS_SQLALCHEMY_INSTALLED:
raise MissingDependencyError(module="sqlalchemy")
from starlite.plugins.sql_alchemy import SQLAlchemyPlugin

from starlite_saqlalchemy.sqlalchemy_plugin import (
Expand Down Expand Up @@ -393,8 +411,6 @@ def configure_worker(self, app_config: AppConfig) -> None:
app_config: The Starlite application config object.
"""
if self.config.do_worker:
if not IS_SAQ_INSTALLED:
raise MissingDependencyError(module="saq", config="worker")
from starlite_saqlalchemy.worker import (
create_worker_instance,
make_service_callback,
Expand Down
25 changes: 9 additions & 16 deletions tests/unit/test_init_plugin_no_extras.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"""Tests for init_plugin.py when no extra dependencies are installed."""

import pytest
from starlite import Starlite
from pydantic import ValidationError

from starlite_saqlalchemy import constants, init_plugin
from starlite_saqlalchemy.exceptions import MissingDependencyError

SKIP = any(
[
Expand All @@ -18,15 +17,15 @@

@pytest.mark.skipif(SKIP, reason="test will only run if no extras are installed")
@pytest.mark.parametrize(
("enabled_config", "error"),
("enabled_config", "error_pattern"),
[
("do_cache", r"^.*\'redis\' is not installed.*$"),
("do_sentry", r"^.*\'sentry_sdk\' is not installed.*$"),
("do_worker", r"^.*\'saq\' is not installed.*$"),
("do_sqlalchemy_plugin", r"^.*\'sqlalchemy\' is not installed.*$"),
("do_cache", r"\'redis\' is not installed."),
("do_sentry", r"\'sentry_sdk\' is not installed."),
("do_worker", r"\'saq\' is not installed."),
("do_sqlalchemy_plugin", r"\'sqlalchemy\' is not installed."),
],
)
def test_extra_dependencies_not_installed(enabled_config: str, error: str) -> None:
def test_extra_dependencies_not_installed(enabled_config: str, error_pattern: str) -> None:
"""Tests that the plugin test required dependencies for switches needing
them."""
kwargs = {
Expand All @@ -45,11 +44,5 @@ def test_extra_dependencies_not_installed(enabled_config: str, error: str) -> No
"do_worker": False,
**{enabled_config: True},
}
config = init_plugin.PluginConfig(**kwargs)

with pytest.raises(MissingDependencyError, match=error):
Starlite(
route_handlers=[],
openapi_config=None,
on_app_init=[init_plugin.ConfigureApp(config=config)],
)
with pytest.raises(ValidationError, match=error_pattern):
init_plugin.PluginConfig(**kwargs)
11 changes: 2 additions & 9 deletions tests/utils/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@

from starlite import Starlite

from starlite_saqlalchemy import ConfigureApp, PluginConfig
from starlite_saqlalchemy import ConfigureApp

from . import controllers


def create_app() -> Starlite:
"""App for our test domain."""
return Starlite(
route_handlers=[controllers.create_router()],
on_app_init=[
ConfigureApp(
config=PluginConfig(do_sqlalchemy_plugin=True, do_worker=True, do_cache=True)
)
],
)
return Starlite(route_handlers=[controllers.create_router()], on_app_init=[ConfigureApp()])

0 comments on commit 4ad9cb3

Please sign in to comment.