Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

mountaineer-cloud

Shared cloud primitives for Python webservices, in particular ones built off of Mountaineer or FastAPI.

Most webapps themselves are cloud-agnostic and can be distributed within any Linux environment. This works pretty well when your whole application is stateless and just talks to a database, but falls down once you actually start needing infrastructure that spawns across your whole cluster. This package exists for the places where that abstraction breaks down and you still need a clean way to work with a real supplier:

  • Object storage
  • Email delivery

Installation

Install the package as usual. If you want additional dependencies to mock (some) providers locally, also install the mocks extra:

uv add "mountaineer-cloud"
uv add --dev "mountaineer-cloud[mocks]"

Storage Primitive

The core convention for our storage primitives is:

  1. A provider package establishes how to connect to the real supplier (S3, R2, etc).
  2. That provider exposes an authenticated *Core object.
  3. A primitive accepts that core object and uses it to perform work.

For storage, that means your field annotation carries the provider core type:

from fastapi import Depends
from iceaxe import Field, TableBase

from mountaineer_cloud import CloudMixin
from mountaineer_cloud.primitives import CloudFile, CloudFileField
from mountaineer_cloud.providers.aws import AWSCore, AWSDependencies


class Asset(CloudMixin, TableBase):
    id: int = Field(primary_key=True)
    file_url: CloudFile[AWSCore] | None = CloudFileField(
        bucket="my-bucket",
        prefix="assets",
    )


async def upload_asset(
    asset: Asset,
    aws: AWSCore = Depends(AWSDependencies.get_aws_core),
) -> bytes:
    await asset.file_url.put_content(aws, b"hello world")
    contents = await asset.file_url.get_content(aws)
    return contents


async def get_asset_contents(
    asset: Asset,
    aws: AWSCore = Depends(AWSDependencies.get_aws_core),
) -> bytes:
    return await asset.file_url.get_content(aws)

If you later move that same model or endpoint to another provider, the primitive API stays the same. Thanks to the magic of typehinting, we'll proactively flag errors if you're trying to use a backend provider that doesn't support the functionality you expect. The main thing that changes is the core type:

  • CloudFile[AWSCore]
  • CloudFile[CloudflareCore]
  • CloudFile[DigitalOceanCore]

CloudFile has support for writing and reading in one fell-swoop, in addition to streaming reads that are more efficient for large files stored remotely.

Email Primitive

Email follows the same convention, except the primitive is a regular embedded Pydantic model instead of a string-backed pointer:

from fastapi import Depends
from iceaxe import Field, TableBase

from mountaineer_cloud import CloudMixin
from mountaineer_cloud.primitives import (
    CloudEmailField,
    EmailBody,
    EmailMessage,
    EmailRecipient,
)
from mountaineer_cloud.providers.resend import ResendCore, ResendDependencies


class Notification(CloudMixin, TableBase):
    id: int = Field(primary_key=True)
    email: EmailMessage[ResendCore] | None = CloudEmailField()


async def send_notification(
    notification: Notification,
    resend: ResendCore = Depends(ResendDependencies.get_resend_core),
):
    notification.email = EmailMessage[ResendCore](
        sender=EmailRecipient(
            email="noreply@example.com",
            display_name="Example App",
        ),
        recipient=EmailRecipient(email="user@example.com"),
        subject="Welcome",
        body=EmailBody(
            text="Welcome to Example App",
            html="<p>Welcome to Example App</p>",
        ),
    )

    return await notification.email.send(resend)

Providers

Each provider module establishes the concrete connection details for an underlying supplier.

Every provider package follows the same pattern:

  • *Config: the settings model you inherit into your downstream app config
  • *Core: the authenticated runtime object that combines provider config with an authenticated session
  • *Dependencies: dependency-injected helpers like get_*_core, useful when using the DI syntax of FastAPI and Mountaineer

This is the center of the convention.

Your endpoint should depend on the provider core, not on a raw client or session. Then your primitive should accept that core and perform the actual work. This keeps supplier-specific connection logic inside the provider package and keeps the primitive surface stable.

Provider Storage Email Config / Core / Dependencies
AWS Yes, via S3 Yes, via SES AWSConfig, AWSCore, AWSDependencies
Cloudflare Yes, via R2 No CloudflareConfig, CloudflareCore, CloudflareDependencies
DigitalOcean Yes, via Spaces No DigitalOceanConfig, DigitalOceanCore, DigitalOceanDependencies
Resend No Yes ResendConfig, ResendCore, ResendDependencies

Environment variables are intentionally omitted here because each provider's settings can evolve over time. The source of truth is the corresponding *Config model in the provider package.

The usage pattern stays the same across providers:

from fastapi import Depends

from mountaineer_cloud.providers.aws import AWSCore, AWSDependencies


async def endpoint(
    aws: AWSCore = Depends(AWSDependencies.get_aws_core),
) -> None:
    ...

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mountaineer_cloud-0.2.0.dev1.tar.gz (972.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mountaineer_cloud-0.2.0.dev1-py3-none-any.whl (48.1 kB view details)

Uploaded Python 3

File details

Details for the file mountaineer_cloud-0.2.0.dev1.tar.gz.

File metadata

  • Download URL: mountaineer_cloud-0.2.0.dev1.tar.gz
  • Upload date:
  • Size: 972.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mountaineer_cloud-0.2.0.dev1.tar.gz
Algorithm Hash digest
SHA256 697d2076caab1f08443af514d23627ec7ad569099c4e668e60146485a36ddadf
MD5 565b150ccc9d3519885b20425703c5f6
BLAKE2b-256 204c8b59c58250fb3a4d64e81128d3bb82ed658497dd9a22b9025d3153bffade

See more details on using hashes here.

Provenance

The following attestation bundles were made for mountaineer_cloud-0.2.0.dev1.tar.gz:

Publisher: test.yml on piercefreeman/mountaineer-cloud

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mountaineer_cloud-0.2.0.dev1-py3-none-any.whl.

File metadata

File hashes

Hashes for mountaineer_cloud-0.2.0.dev1-py3-none-any.whl
Algorithm Hash digest
SHA256 21145bdf845986046711c965126a5ba5ed6a88d08d555dfb418eff16c201a0b5
MD5 d7fe2250bf9d3b9e75f37790ca042b44
BLAKE2b-256 02748d3ba88e6d4e44045377173140d3c3e722a7d83b41d62d56b09b57a504d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for mountaineer_cloud-0.2.0.dev1-py3-none-any.whl:

Publisher: test.yml on piercefreeman/mountaineer-cloud

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0.dev1 This release

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page