Skip to content
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
13 changes: 10 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `CatalogInfo`, `NamespaceInfo`, `TableInfo`, `ColumnInfo` — shared physical metadata dataclasses

3. **Settings** (`src/mountainash_data/core/settings/`)
- Declarative per-backend descriptors (`BackendDescriptor` + `ParameterSpec` list).
- Typed discriminated-union auth via `AuthSpec` subclasses (`PasswordAuth`, `OAuth2Auth`, `IAMAuth`, …).
- Backend shell classes register themselves via `@register` and expose `to_driver_kwargs()` + `to_connection_string()`.
- Database-flavored layer over `mountainash-settings`'s `profiles` and
`auth` sub-packages.
- `BackendDescriptor` is a typed `ProfileDescriptor` subclass adding
`default_port` / `connection_string_scheme` / `ibis_dialect` / `rides_on`;
every backend is a two-line shell registered via `@register`.
- `ConnectionProfile` adds `to_driver_kwargs()` and `to_connection_string()`
on top of the generic `DescriptorProfile` base.
- `AuthSpec` subclasses (`PasswordAuth`, `OAuth2Auth`, `IAMAuth`, …) live
upstream in `mountainash_settings.auth` and are re-exported from
`mountainash_data.core.settings` for downstream compatibility.
- Composite driver mappings live in `settings/adapters/<backend>.py`.

4. **Factories** (`src/mountainash_data/core/factories/`)
Expand Down
6 changes: 4 additions & 2 deletions src/mountainash_data/core/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ class body is a two-line shell (``__descriptor__`` + ``__adapter__``).
from .descriptor import MISSING, BackendDescriptor, ParameterSpec
from .profile import ConnectionProfile
from .registry import (
DATABASES_REGISTRY,
REGISTRY,
get_descriptor,
get_settings_class,
register,
)

# Auth union members
from .auth import (
from mountainash_settings.auth import (
AuthSpec,
AzureADAuth,
CertificateAuth,
Expand Down Expand Up @@ -49,7 +50,8 @@ class body is a two-line shell (``__descriptor__`` + ``__adapter__``).
__all__ = [
# primitives
"MISSING", "BackendDescriptor", "ParameterSpec", "ConnectionProfile",
"REGISTRY", "get_descriptor", "get_settings_class", "register",
"DATABASES_REGISTRY", "REGISTRY",
"get_descriptor", "get_settings_class", "register",
# auth
"AuthSpec", "NoAuth", "PasswordAuth", "TokenAuth", "JWTAuth",
"OAuth2Auth", "ServiceAccountAuth", "IAMAuth", "WindowsAuth",
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/bigquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

import typing as t

from mountainash_data.core.settings.auth import NoAuth, ServiceAccountAuth
from mountainash_settings.auth import NoAuth, ServiceAccountAuth

if t.TYPE_CHECKING:
from mountainash_data.core.settings.bigquery import BigQueryAuthSettings


def build_driver_kwargs(profile: "BigQueryAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()

auth = profile.auth
if isinstance(auth, ServiceAccountAuth):
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/mssql.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typing as t

from mountainash_data.core.settings.auth import (
from mountainash_settings.auth import (
AzureADAuth,
PasswordAuth,
WindowsAuth,
Expand All @@ -15,7 +15,7 @@


def build_driver_kwargs(profile: "MSSQLAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()

# Instance name → host\instance
if profile.INSTANCE_NAME:
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

def build_driver_kwargs(profile: "MySQLAuthSettings") -> dict[str, t.Any]:
"""Assemble driver kwargs, including ssl={} dict if any SSL fields are set."""
kwargs = profile._default_driver_kwargs()
kwargs.update(profile._auth_to_driver_kwargs())
kwargs = profile._default_kwargs()
kwargs.update(profile._auth_kwargs())

if profile.SSL_MODE is not None:
kwargs["ssl_mode"] = str(profile.SSL_MODE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typing as t

from mountainash_data.core.settings.auth import OAuth2Auth, TokenAuth
from mountainash_settings.auth import OAuth2Auth, TokenAuth

if t.TYPE_CHECKING:
from mountainash_data.core.settings.pyiceberg_rest import (
Expand All @@ -13,7 +13,7 @@


def build_driver_kwargs(profile: "PyIcebergRestAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()

# S3 family
for field, key in [
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/redshift.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

import typing as t

from mountainash_data.core.settings.auth import IAMAuth, PasswordAuth
from mountainash_settings.auth import IAMAuth, PasswordAuth

if t.TYPE_CHECKING:
from mountainash_data.core.settings.redshift import RedshiftAuthSettings


def build_driver_kwargs(profile: "RedshiftAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()

auth = profile.auth
if isinstance(auth, PasswordAuth):
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typing as t

from mountainash_data.core.settings.auth import (
from mountainash_settings.auth import (
CertificateAuth,
OAuth2Auth,
PasswordAuth,
Expand All @@ -16,7 +16,7 @@


def build_driver_kwargs(profile: "SnowflakeAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()

# Session parameters
session_params: dict[str, t.Any] = {}
Expand Down
4 changes: 2 additions & 2 deletions src/mountainash_data/core/settings/adapters/trino.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import typing as t

from mountainash_data.core.settings.auth import (
from mountainash_settings.auth import (
JWTAuth,
KerberosAuth,
NoAuth,
Expand All @@ -16,7 +16,7 @@


def build_driver_kwargs(profile: "TrinoAuthSettings") -> dict[str, t.Any]:
kwargs = profile._default_driver_kwargs()
kwargs = profile._default_kwargs()
auth = profile.auth
if isinstance(auth, PasswordAuth):
from trino.auth import BasicAuthentication
Expand Down
53 changes: 35 additions & 18 deletions src/mountainash_data/core/settings/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
"""Discriminated-union AuthSpec members."""
"""Compatibility shim — re-exports from mountainash_settings.auth.

from .azure import AzureADAuth, WindowsAuth
from .base import AuthSpec
from .certificate import CertificateAuth
from .iam import IAMAuth
from .kerberos import KerberosAuth
from .none import NoAuth
from .oauth2 import OAuth2Auth
from .password import PasswordAuth
from .service_account import ServiceAccountAuth
from .token import JWTAuth, TokenAuth
The auth primitives (AuthSpec subclasses, auth_to_driver_kwargs) now live in
the upstream mountainash-settings package. This module re-exports everything
so existing imports of the form::

from mountainash_data.core.settings.auth import NoAuth

continue to work unchanged while the rest of the codebase migrates.
"""

from mountainash_settings.auth import (
AUTH_TO_DRIVER_KWARGS,
AuthSpec,
AzureADAuth,
CertificateAuth,
IAMAuth,
JWTAuth,
KerberosAuth,
NoAuth,
OAuth2Auth,
PasswordAuth,
ServiceAccountAuth,
TokenAuth,
WindowsAuth,
auth_to_driver_kwargs,
)

__all__ = [
"AUTH_TO_DRIVER_KWARGS",
"AuthSpec",
"NoAuth",
"PasswordAuth",
"TokenAuth",
"AzureADAuth",
"CertificateAuth",
"IAMAuth",
"JWTAuth",
"KerberosAuth",
"NoAuth",
"OAuth2Auth",
"PasswordAuth",
"ServiceAccountAuth",
"IAMAuth",
"TokenAuth",
"WindowsAuth",
"AzureADAuth",
"KerberosAuth",
"CertificateAuth",
"auth_to_driver_kwargs",
]
30 changes: 0 additions & 30 deletions src/mountainash_data/core/settings/auth/azure.py

This file was deleted.

20 changes: 2 additions & 18 deletions src/mountainash_data/core/settings/auth/base.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
"""Base class for discriminated-union auth specifications."""
"""Compatibility shim — re-exports from mountainash_settings.auth.base."""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict
from mountainash_settings.auth.base import AuthSpec

__all__ = ["AuthSpec"]


class AuthSpec(BaseModel):
"""Abstract tagged base for authentication specifications.

Each concrete subclass declares its own ``kind: Literal["..."]`` field;
this base intentionally does not declare one. When composed into a
:class:`pydantic.Field` discriminated union, pydantic looks up ``kind``
on each member, not on a shared base, so removing it here also closes
Pyright's ``reportIncompatibleVariableOverride`` warnings on every
subclass.
"""

model_config = ConfigDict(extra="forbid", frozen=True)
21 changes: 0 additions & 21 deletions src/mountainash_data/core/settings/auth/certificate.py

This file was deleted.

79 changes: 2 additions & 77 deletions src/mountainash_data/core/settings/auth/dispatch.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,5 @@
"""Default mapping from AuthSpec instances to driver kwargs.
"""Compatibility shim — re-exports from mountainash_settings.auth.dispatch."""

Backend adapters can override individual auth types by consulting this map or
by writing bespoke match statements. The defaults cover the common case.
"""

from __future__ import annotations

import typing as t

from .base import AuthSpec
from .iam import IAMAuth
from .none import NoAuth
from .oauth2 import OAuth2Auth
from .password import PasswordAuth
from .token import JWTAuth, TokenAuth
from mountainash_settings.auth.dispatch import AUTH_TO_DRIVER_KWARGS, auth_to_driver_kwargs

__all__ = ["AUTH_TO_DRIVER_KWARGS", "auth_to_driver_kwargs"]


def _noauth(_auth: NoAuth) -> dict[str, t.Any]:
return {}


def _password(auth: PasswordAuth) -> dict[str, t.Any]:
return {
"user": auth.username,
"password": auth.password.get_secret_value(),
}


def _token(auth: TokenAuth) -> dict[str, t.Any]:
return {"token": auth.token.get_secret_value()}


def _jwt(auth: JWTAuth) -> dict[str, t.Any]:
return {"token": auth.token.get_secret_value()}


def _oauth2(auth: OAuth2Auth) -> dict[str, t.Any]:
if auth.token is not None:
return {"token": auth.token.get_secret_value()}
if auth.client_id is not None and auth.client_secret is not None:
return {"credential": f"{auth.client_id}:{auth.client_secret.get_secret_value()}"}
return {}


def _iam(auth: IAMAuth) -> dict[str, t.Any]:
"""Empty dict means 'use ambient AWS credentials' (env vars, instance profile, SSO)."""
out: dict[str, t.Any] = {}
if auth.role_arn is not None:
out["iam_role_arn"] = auth.role_arn
if auth.access_key_id is not None:
out["aws_access_key_id"] = auth.access_key_id
if auth.secret_access_key is not None:
out["aws_secret_access_key"] = auth.secret_access_key.get_secret_value()
if auth.session_token is not None:
out["aws_session_token"] = auth.session_token.get_secret_value()
return out


AUTH_TO_DRIVER_KWARGS: dict[type[AuthSpec], t.Callable[[t.Any], dict[str, t.Any]]] = {
NoAuth: _noauth,
PasswordAuth: _password,
TokenAuth: _token,
JWTAuth: _jwt,
OAuth2Auth: _oauth2,
IAMAuth: _iam,
# WindowsAuth, AzureADAuth, KerberosAuth, ServiceAccountAuth, CertificateAuth:
# no sensible default — their respective backend adapters handle mapping.
}


def auth_to_driver_kwargs(auth: AuthSpec) -> dict[str, t.Any]:
"""Look up the default mapper for ``auth`` and produce driver kwargs.

Raises:
KeyError: if no mapper is registered for ``type(auth)``.
"""
return AUTH_TO_DRIVER_KWARGS[type(auth)](auth)
Loading
Loading