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
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "supabase-py",
"image": "mcr.microsoft.com/devcontainers/python:3.11",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": { "moby": false },
"ghcr.io/devcontainers/features/github-cli:1": {},
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/ci_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import json
import sys

PYTHON_VERSIONS = ["3.11", "3.12", "3.13", "3.14", "3.15"]
PYTHON_VERSIONS = ["3.12", "3.13", "3.14", "3.15"]
LOWEST_PYTHON_VERSION = PYTHON_VERSIONS[0]
PACKAGES = [
"functions",
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: "3.11"
python-version: "3.12"

- name: Check lint
run: uv run --frozen ruff check --output-format=github
Expand All @@ -72,7 +72,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: "3.11"
python-version: "3.12"

- name: Import each package without httpx
run: |
Expand Down Expand Up @@ -253,7 +253,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: "3.11"
python-version: "3.12"

- name: Build all packages and publish
run: make publish
2 changes: 1 addition & 1 deletion .github/workflows/codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: "3.11"
python-version: "3.12"

- name: Regenerate sync code
run: make auth.build-sync
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Setup and commands: [README.md § Local Development](README.md#local-development

- `make <pkg>.tests` runs pyrefly and pytest. For `auth`, `postgrest`, `storage` and `realtime`, pytest first starts Docker / Supabase CLI infra; `make stop-infra` when done.
- Fast loop for tests that need no infra: from `src/<pkg>`, `uv run --frozen --package <import name> pytest tests/<file>.py`.
- Code must run on Python 3.11, CI tests 3.11 to 3.15 ([ci.yml](.github/workflows/ci.yml)).
- Code must run on Python 3.12, CI tests 3.12 to 3.15 ([ci.yml](.github/workflows/ci.yml)).

## Public API

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Relevant links:

### Python

We support every Python version that has not yet reached end of life, as listed in the [Python version status page](https://devguide.python.org/versions/). The currently supported versions are the ones tested in [CI](.github/workflows/ci.yml).
We support Python 3.12 and every later version that has not yet reached end of life, as listed in the [Python version status page](https://devguide.python.org/versions/). The currently supported versions are the ones tested in [CI](.github/workflows/ci.yml).

Once a Python version reaches end of life, support for it may be dropped in a **minor release**, and **this won't be considered a breaking change**. For instance, Python 3.9 reached end of life in October 2025, so it can be removed in a minor update.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ supabase = { workspace = true }
asyncio_mode = "auto"

[tool.ruff]
target-version = "py311"
target-version = "py312"

[tool.ruff.lint]
select = [
Expand Down
4 changes: 1 addition & 3 deletions src/auth/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,16 @@ readme = "README.md"
license = "MIT"
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Operating System :: OS Independent",
]
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
"pydantic >=2,<3",
"pyjwt[crypto] >=2.15.1",
"typing-extensions >=4.15.0",
"supabase_utils == 3.0.0-a1", # x-release-please-version
]

Expand Down
8 changes: 3 additions & 5 deletions src/auth/src/supabase_auth/admin_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

from dataclasses import dataclass
from types import TracebackType
from typing import Generic

from supabase_utils.http.headers import Headers
from supabase_utils.http.io import (
AsyncHttpIO,
AsyncHttpSession,
HttpIO,
HttpMethod,
HttpSession,
SyncHttpIO,
Expand Down Expand Up @@ -48,7 +46,7 @@


@dataclass
class SupabaseAuthAdminMFA(Generic[HttpIO]):
class SupabaseAuthAdminMFA[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
"""
Contains the full multi-factor authentication administration API.
"""
Expand Down Expand Up @@ -90,7 +88,7 @@ def list_factors(self, user_id: str) -> HttpMethod[AuthMFAAdminListFactorsRespon


@dataclass
class SupabaseAuthAdminOAuth(Generic[HttpIO]):
class SupabaseAuthAdminOAuth[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
"""
Contains all OAuth client administration methods.
Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
Expand Down Expand Up @@ -248,7 +246,7 @@ def regenerate_client_secret(
return OAuthClientResponse(client=_validate_model(response, OAuthClient))


class SupabaseAuthAdmin(Generic[HttpIO]):
class SupabaseAuthAdmin[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
def __init__(
self, executor: HttpIO, base_url: URL, default_headers: Headers
) -> None:
Expand Down
12 changes: 5 additions & 7 deletions src/auth/src/supabase_auth/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections.abc import Callable, Mapping
from dataclasses import dataclass
from types import TracebackType
from typing import Any, Generic, Literal, TypeVar
from typing import Any, Literal, override
from uuid import uuid4

from jwt import get_algorithm_by_name
Expand All @@ -13,7 +13,6 @@
from supabase_utils.http.io import (
AsyncHttpIO,
AsyncHttpSession,
HttpIO,
HttpMethod,
HttpSession,
SyncHttpIO,
Expand All @@ -22,7 +21,6 @@
from supabase_utils.http.query import URLQuery
from supabase_utils.http.request import EmptyRequest, JSONRequest, Response
from supabase_utils.types import JSON
from typing_extensions import override
from yarl import URL

from .errors import (
Expand Down Expand Up @@ -118,11 +116,11 @@ def _strip_password_recovery_suffix(code_verifier: str) -> str:
return code_verifier.split("/", 1)[0]


SessionManager = TypeVar("SessionManager", bound=_SessionManagerCommon[Any])


@dataclass
class SupabaseAuthHttpClient(Generic[HttpIO, SessionManager]):
class SupabaseAuthHttpClient[
HttpIO: (SyncHttpIO, AsyncHttpIO),
SessionManager: _SessionManagerCommon[Any],
]:
executor: HttpIO
base_url: URL
default_headers: Headers
Expand Down
11 changes: 2 additions & 9 deletions src/auth/src/supabase_auth/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from base64 import urlsafe_b64decode
from dataclasses import dataclass
from datetime import datetime
from typing import TypeVar
from urllib.parse import urlparse

from pydantic import BaseModel, TypeAdapter, ValidationError
Expand Down Expand Up @@ -97,20 +96,14 @@ def parse_jwks(response: Response) -> JWKSet:
return jwk


Model = TypeVar("Model", bound=BaseModel)


def _validate_model(response: Response, model: type[Model]) -> Model:
def _validate_model[Model: BaseModel](response: Response, model: type[Model]) -> Model:
if response.is_success:
return model.model_validate_json(response.content)
else:
raise _handle_error_response(response)


Inner = TypeVar("Inner")


def _validate_adapter(response: Response, adapter: TypeAdapter[Inner]) -> Inner:
def _validate_adapter[Inner](response: Response, adapter: TypeAdapter[Inner]) -> Inner:
if response.is_success:
return adapter.validate_json(response.content)
else:
Expand Down
5 changes: 2 additions & 3 deletions src/auth/src/supabase_auth/mfa.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Generic, Literal
from typing import Literal

from supabase_utils.http.headers import Headers
from supabase_utils.http.io import (
AsyncHttpIO,
HttpIO,
HttpMethod,
SyncHttpIO,
handle_http_io,
Expand Down Expand Up @@ -34,7 +33,7 @@


@dataclass
class SupabaseAuthMFAHttpClient(Generic[HttpIO]):
class SupabaseAuthMFAHttpClient[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
"""
Contains the full multi-factor authentication API http calls
"""
Expand Down
4 changes: 1 addition & 3 deletions src/auth/src/supabase_auth/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from types import TracebackType
from typing import Generic

from supabase_utils.http.headers import Headers
from supabase_utils.http.io import (
AsyncHttpIO,
HttpIO,
HttpMethod,
SyncHttpIO,
handle_http_io,
Expand Down Expand Up @@ -67,7 +65,7 @@ async def remove_item(self, key: str) -> None:


@dataclass
class _SessionManagerCommon(Generic[HttpIO]):
class _SessionManagerCommon[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
"""
Common methods shared between sync and async implementations
of the session manager.
Expand Down
4 changes: 2 additions & 2 deletions src/auth/tests/_async/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from collections.abc import AsyncGenerator
from dataclasses import dataclass
from random import random
from time import time
from typing import NotRequired
from typing import NotRequired, TypedDict

import pytest
from jwt import encode
from typing_extensions import AsyncGenerator, TypedDict

from supabase_auth import AsyncSupabaseAuthAdmin, AsyncSupabaseAuthClient
from supabase_auth.types import (
Expand Down
4 changes: 2 additions & 2 deletions src/auth/tests/_sync/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from collections.abc import Generator
from dataclasses import dataclass
from random import random
from time import time
from typing import NotRequired
from typing import NotRequired, TypedDict

import pytest
from jwt import encode
from typing_extensions import Generator, TypedDict

from supabase_auth import SyncSupabaseAuthAdmin, SyncSupabaseAuthClient
from supabase_auth.types import (
Expand Down
3 changes: 1 addition & 2 deletions src/functions/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ license = "MIT"
readme = "README.md"
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Operating System :: OS Independent",
]
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
"pydantic>=2.12.2",
"supabase-utils==3.0.0-a1", # x-release-please-version
Expand Down
5 changes: 2 additions & 3 deletions src/functions/src/supabase_functions/client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from collections.abc import Mapping
from typing import Generic, Literal, cast, overload
from typing import Literal, cast, overload

from supabase_utils.http.headers import Headers
from supabase_utils.http.io import (
AsyncHttpIO,
AsyncHttpSession,
HttpIO,
HttpMethod,
HttpSession,
SyncHttpIO,
Expand All @@ -32,7 +31,7 @@
from .version import __version__


class FunctionsClient(Generic[HttpIO]):
class FunctionsClient[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
def __init__(self, url: URL, headers: Mapping[str, str], executor: HttpIO) -> None:
if not (url.scheme == "http" or url.scheme == "https"):
raise ValueError("url must be a valid HTTP URL string")
Expand Down
2 changes: 1 addition & 1 deletion src/postgrest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

### Requirements

- Python >= 3.11
- Python >= 3.12
Comment thread
spydon marked this conversation as resolved.
Resolved
- PostgreSQL >= 13
- PostgREST >= 11

Expand Down
2 changes: 1 addition & 1 deletion src/postgrest/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Installation
============
Requirements:

- Python >= 3.11
- Python >= 3.12

**With pip:**
::
Expand Down
4 changes: 1 addition & 3 deletions src/postgrest/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ readme = "README.md"
license = "MIT"
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Operating System :: OS Independent",
]
requires-python = ">=3.11"
requires-python = ">=3.12"
dependencies = [
"pydantic >=2,<3",
"typing-extensions >=4.15.0",
"yarl>=1.20.1",
"supabase_utils==3.0.0-a1", # x-release-please-version
]
Expand Down
5 changes: 2 additions & 3 deletions src/postgrest/src/postgrest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
from base64 import b64encode
from collections.abc import Mapping
from types import TracebackType
from typing import Generic, Literal, Self, overload
from typing import Literal, Self, overload

from supabase_utils.http.headers import Headers
from supabase_utils.http.io import (
AsyncHttpIO,
AsyncHttpSession,
HttpIO,
HttpSession,
SyncHttpIO,
)
Expand All @@ -29,7 +28,7 @@
from .version import __version__


class PostgrestClient(Generic[HttpIO]):
class PostgrestClient[HttpIO: (SyncHttpIO, AsyncHttpIO)]:
"""PostgREST client."""

def __init__(
Expand Down
3 changes: 1 addition & 2 deletions src/postgrest/src/postgrest/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from typing import Any
from typing import Any, override

from pydantic import BaseModel
from supabase_utils.http.request import Response
from typing_extensions import override


class APIErrorFromJSON(BaseModel):
Expand Down
Loading
Loading