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

Require dependencies based on plugin config #213

Merged
merged 31 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6fe1887
♻️ refactor(deps): require dependencies based on plugin config
gazorby Jan 8, 2023
8fc9720
🐛 fix: tox config
gazorby Jan 8, 2023
a2f84fb
♻️ refactor(tox): append coverage in testenv:noextras
gazorby Jan 8, 2023
cb90b5f
♻️ refactor(tox): don't run coverage in parallel mode in testenv:noex…
gazorby Jan 8, 2023
ae2c67c
🐛 fix: full coverage
gazorby Jan 8, 2023
9297233
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 12, 2023
a7c8c35
♻️ refactor: apply changes from review
gazorby Jan 12, 2023
bc63b10
🐛 fix: linters
gazorby Jan 12, 2023
8f00084
♻️ refactor: full coverage
gazorby Jan 12, 2023
f5d276c
♻️ refactor: update pytest plugin fixtures
gazorby Jan 16, 2023
2b5d9c4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2023
c18e509
✅ test: fix tests
gazorby Jan 17, 2023
83df0ba
♻️ refactor: add sqlalchemy to optional dependencies
gazorby Jan 17, 2023
47e4670
♻️ refactor: silent flake8
gazorby Jan 17, 2023
4a98d70
✅ test(tox): make noextras passing the full test suite
gazorby Jan 17, 2023
143030a
♻️ refactor: silent pyright
gazorby Jan 17, 2023
1be7382
📝 docs: update readme
gazorby Jan 17, 2023
978f61e
📝 docs: update readme
gazorby Jan 17, 2023
f5c31a5
refactor: requirements/tox/etc
peterschutt Jan 18, 2023
b446f52
style: rollback style changes
peterschutt Jan 18, 2023
abfa2ea
refactor: run pytest plugin tests in sub-process.
peterschutt Jan 18, 2023
273a8ab
refactor: no top level conditional imports.
peterschutt Jan 18, 2023
2381cd7
refactor: makes lifespan check imports and logic conditional
peterschutt Jan 18, 2023
53e1f23
refactor: no implicit imports of modules with optional functionality
peterschutt Jan 18, 2023
681ae16
refactor: move any worker related stuff out of service module
peterschutt Jan 18, 2023
4ad9cb3
refactor: default_factory and validator for dependency-based gates
peterschutt Jan 18, 2023
da3e4e6
refactor: remove more module scoped conditional imports.
peterschutt Jan 18, 2023
f0f2539
Merge branch '0.29' into build/extra-dependencies
peterschutt Jan 18, 2023
02d3a88
refactor: organise tests according to dependencies (#257)
peterschutt Jan 18, 2023
65ebdaf
refactor: continue splitting tests up by required dependency.
peterschutt Jan 19, 2023
b1f9aa7
refactor: add method for setting lifecycle handlers.
peterschutt Jan 19, 2023
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
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ For example:
- `tox -e coverage` - unit test coverage report.
- `tox -e mypy` - runs mypy static type checker on the source.
- `tox -e pyright` - runs pyright static type checker on the source.
- `tox -e refurb` - runs the [refurb](https://github.com/dosisod/refurb) tool over the source.
- `tox -e integration` - runs the dockerized integration test suite.
- `tox` - run everything, you maniac!

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ Configuration for a [Starlite](https://github.com/starlite-api/starlite) applica
- SAQ async worker
- Lots of features!

## Installation

This will install `starlite-saqlalchemy` with minimal dependencies.

```console
poetry add starlite-saqlalchemy
```

You can also install additional dependencies depending on the features you need:

```console
# Repository implementation, DTOs
poetry add starlite-saqlalchemy[sqlalchemy]
# Async worker using saq
poetry add starlite-saqlalchemy[worker]
# Redis cache backend
poetry add starlite-saqlalchemy[cache]
# Sentry integration for starlite
poetry add starlite-saqlalchemy[sentry]

# or to install them all:
poetry add starlite-saqlalchemy[all]
```

## Example

```python
Expand Down
184 changes: 78 additions & 106 deletions poetry.lock

Large diffs are not rendered by default.

32 changes: 24 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,29 @@ packages = [
[tool.poetry.dependencies]
python = "^3.10"
asyncpg = "*"
hiredis = "*"
httpx = "*"
msgspec = "*"
pydantic = "*"
python-dotenv = "*"
redis = "*"
saq = "^0.9.1"
sentry-sdk = ">=1.13.0"
sqlalchemy = "==2.0.0rc2"
starlite = "^1.40.1"
structlog = ">=22.2.0"
tenacity = "*"
uvicorn = "*"
uvloop = "*"
structlog = ">=22.2.0"

# Optionals
hiredis = { version = "*", optional = true }
redis = { version = "*", optional = true }
saq = { version = "^0.9.1", optional = true }
sentry-sdk = { version = "*", optional = true }
sqlalchemy = { version = "==2.0.0rc2", optional = true }

[tool.poetry.extras]
cache = ["redis", "hiredis"]
worker = ["saq", "hiredis"]
sentry = ["sentry-sdk"]
sqlalchemy = ["sqlalchemy"]
all = ["redis", "hiredis", "saq", "sentry-sdk", "sqlalchemy"]

peterschutt marked this conversation as resolved.
Show resolved Hide resolved
[tool.poetry.plugins."pytest11"]
pytest_starlite_saqlalchemy = "pytest_starlite_saqlalchemy"
Expand All @@ -92,11 +101,18 @@ add-select = "D401,D404,D417"
convention = "google"

[tool.pytest.ini_options]
addopts = ["-ra", "--strict-config"]
addopts = [
peterschutt marked this conversation as resolved.
Show resolved Hide resolved
"-ra",
"--strict-config",
# Plugin are enabled in tests/conftest.py to control loading order.
"-p",
"no:pytest_starlite_saqlalchemy",
"-p",
"no:pytest_dotenv",
]
asyncio_mode = "auto"
env_files = ["tests.env"]
testpaths = ["tests/unit"]
test_app = "tests.utils.app:create_app"

[tool.pylint.main]
disable = [
Expand Down
7 changes: 7 additions & 0 deletions requirements.dev-extras.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-r requirements.dev.txt

sentry-sdk >= "1.13.0"
hiredis
redis
saq >= "0.9.1"
sqlalchemy == 2.0.0rc2
10 changes: 6 additions & 4 deletions src/pytest_starlite_saqlalchemy/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# pylint: disable=import-outside-toplevel
from __future__ import annotations

import os
import re
from typing import TYPE_CHECKING
from unittest.mock import MagicMock
Expand All @@ -12,6 +13,8 @@
from structlog.testing import CapturingLogger
from uvicorn.importer import ImportFromStringError, import_from_string

from starlite_saqlalchemy import constants

if TYPE_CHECKING:
from collections.abc import Generator

Expand All @@ -35,7 +38,7 @@ def pytest_addoption(parser: Parser) -> None:
"test_app",
"Path to application instance, or callable that returns an application instance.",
type="string",
default="app.main:create_app",
default=os.environ.get("TEST_APP", "app.main:create_app"),
peterschutt marked this conversation as resolved.
Show resolved Hide resolved
)
parser.addini(
"unit_test_pattern",
Expand Down Expand Up @@ -65,7 +68,7 @@ def _patch_http_close(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(starlite_saqlalchemy.http, "clients", set())


@pytest.fixture(autouse=True)
@pytest.fixture(autouse=constants.IS_SQLALCHEMY_INSTALLED)
def _patch_sqlalchemy_plugin(is_unit_test: bool, monkeypatch: MonkeyPatch) -> None:
if is_unit_test:
from starlite_saqlalchemy import sqlalchemy_plugin
Expand All @@ -77,7 +80,7 @@ def _patch_sqlalchemy_plugin(is_unit_test: bool, monkeypatch: MonkeyPatch) -> No
)


@pytest.fixture(autouse=True)
@pytest.fixture(autouse=constants.IS_SAQ_INSTALLED)
def _patch_worker(is_unit_test: bool, monkeypatch: MonkeyPatch) -> None:
"""We don't want the worker to start for unittests."""
if is_unit_test:
Expand All @@ -94,7 +97,6 @@ def fx_app(pytestconfig: Config, monkeypatch: MonkeyPatch) -> Starlite:
An application instance, configured via plugin.
"""
test_app_str = pytestconfig.getini("test_app")

try:
app_or_callable = import_from_string(test_app_str)
except (ImportFromStringError, ModuleNotFoundError):
Expand Down
15 changes: 1 addition & 14 deletions src/starlite_saqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,35 @@ def example_handler() -> dict:
# this is because pycharm wigs out when there is a module called `exceptions`:
# noinspection PyCompatibility
from . import (
cache,
compression,
db,
dependencies,
dto,
exceptions,
health,
http,
log,
openapi,
redis,
repository,
sentry,
service,
settings,
sqlalchemy_plugin,
type_encoders,
worker,
)
from .init_plugin import ConfigureApp, PluginConfig

__all__ = [
"ConfigureApp",
"PluginConfig",
"cache",
"compression",
"db",
"dependencies",
"dto",
"exceptions",
"health",
"http",
"log",
"openapi",
"redis",
"repository",
"sentry",
"service",
"settings",
"sqlalchemy_plugin",
"type_encoders",
"worker",
]


__version__ = "0.28.1"
39 changes: 39 additions & 0 deletions src/starlite_saqlalchemy/constants.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
"""Application constants."""
from __future__ import annotations

from importlib import import_module
from typing import TYPE_CHECKING, Any

from starlite_saqlalchemy.settings import app
from starlite_saqlalchemy.utils import case_insensitive_string_compare

if TYPE_CHECKING:
from collections.abc import MutableMapping

from starlite_saqlalchemy.service import Service


IS_TEST_ENVIRONMENT = case_insensitive_string_compare(app.ENVIRONMENT, app.TEST_ENVIRONMENT_NAME)
"""Flag indicating if the application is running in a test environment."""

IS_LOCAL_ENVIRONMENT = case_insensitive_string_compare(app.ENVIRONMENT, app.LOCAL_ENVIRONMENT_NAME)
"""Flag indicating if application is running in local development mode."""

IS_REDIS_INSTALLED = True
"""Flag indicating if redis module is installed."""

IS_SAQ_INSTALLED = True
"""Flag indicating if saq module is installed."""

IS_SENTRY_SDK_INSTALLED = True
"""Flag indicating if sentry_sdk module is installed."""

IS_SQLALCHEMY_INSTALLED = True
"""Flag indicating if sqlalchemy module is installed."""


for package in ("redis", "saq", "sentry_sdk", "sqlalchemy"):
try:
import_module(package)
except ModuleNotFoundError:
match package:
case "redis":
IS_REDIS_INSTALLED = False
case "saq":
IS_SAQ_INSTALLED = False
case "sentry_sdk":
IS_SENTRY_SDK_INSTALLED = False
case "sqlalchemy": # pragma: no cover
IS_SQLALCHEMY_INSTALLED = False

SERVICE_OBJECT_IDENTITY_MAP: MutableMapping[str, type[Service[Any]]] = {}
"""Used by the worker to lookup methods for service object callbacks."""
11 changes: 11 additions & 0 deletions src/starlite_saqlalchemy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ class AuthorizationError(StarliteSaqlalchemyClientError):
"""A user tried to do something they shouldn't have."""


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

def __init__(self, module: str, config: str | None = None) -> None:
config = config if config else module
super().__init__(
f"You enabled {config} configuration but package {module!r} is not installed. "
f'You may need to run: "poetry install starlite-saqlalchemy[{config}]"'
)


class HealthCheckConfigurationError(StarliteSaqlalchemyError):
"""An error occurred while registering an health check."""

Expand Down
Loading