Skip to content

An abstract base shares one provider's declarations - #301

Merged
czpython merged 1 commit into
mainfrom
abstract-service-bases
Aug 22, 2026
Merged

An abstract base shares one provider's declarations#301
czpython merged 1 commit into
mainfrom
abstract-service-bases

Conversation

@czpython

Copy link
Copy Markdown
Owner

One provider often backs several services — Google backs Gmail and Google Calendar, and each keeps its own card, its own key, and its own blast radius. Until now every one of them re-declared the provider's OAuth quirks: both endpoints, extra_authorize_params = {"access_type": "offline", "prompt": "consent"}, the identity endpoint and its scopes, and the same two-field Settings.

Service now accepts abstract = True. An abstract base never registers and skips validation; each subclass inherits everything the base declares — Settings included, since the validator now reads Settings through inheritance — and registers itself:

class GoogleOauth(Service):
    abstract = True
    authorization_endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
    token_endpoint = "https://oauth2.googleapis.com/token"
    extra_authorize_params = {"access_type": "offline", "prompt": "consent"}
    identity_endpoint = "https://openidconnect.googleapis.com/v1/userinfo"
    identity_scopes = ("openid", "email")

    class Settings(BaseModel):
        client_id: str = Field(title="Client ID")
        client_secret: SecretStr = Field(title="Client secret")


class Gmail(GoogleOauth):
    name = "gmail"
    title = "Gmail"

The contract stays loud: abstract must be set in the class's own body (it never leaks to children), a class that sets both abstract and name raises at definition, a concrete subclass that forgets name still raises, and a base declaring only one OAuth endpoint fails on its first concrete subclass — the endpoint checks read through the MRO. Typing improves too: the base's declarations are checked against Service's ClassVar annotations, so a wrong-shaped quirk fails pyright at the definition site instead of surfacing at runtime.

@czpython
czpython force-pushed the abstract-service-bases branch from b0c7b89 to a1bdc69 Compare August 22, 2026 08:41
@czpython
czpython enabled auto-merge (squash) August 22, 2026 08:41
@czpython
czpython force-pushed the abstract-service-bases branch from a1bdc69 to 43216b5 Compare August 22, 2026 08:44
@czpython
czpython merged commit 40b4153 into main Aug 22, 2026
1 check passed
@czpython
czpython deleted the abstract-service-bases branch August 22, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant