Skip to main content
Pre-release

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

effect-py

Effect-TS style typed effects for Python. effect-py brings the Effect programming model — Effect[A, E, R] with tracked success values, tracked errors, and tracked dependencies, plus services and layers for dependency injection — to Python, checked end-to-end by pyright in strict mode.

from dataclasses import dataclass
from typing import Protocol

from effect_py import fn, layer, provide, run_sync, service, succeed
from effect_py.errors import TaggedError, catch_tag


# tags are Protocol classes -- impls conform structurally, no subclassing
class Database(Protocol):
    def find(self, user_id: str) -> str | None: ...


class InMemoryDatabase:
    def __init__(self, rows: dict[str, str]) -> None:
        self.rows = rows

    def find(self, user_id: str) -> str | None:
        return self.rows.get(user_id)


@dataclass
class UserNotFound(TaggedError):
    user_id: str


@fn("Users.greet")
def greet(user_id: str):
    db = yield from service(Database)          # R channel: requires Database
    name = db.find(user_id)
    if name is None:
        return (yield from UserNotFound(user_id))  # E channel: may fail
    return f"Hello, {name}!"

# greet("u1") : Effect[str, UserNotFound, Database] — fully inferred, zero annotations

program = greet("u1").pipe(
    catch_tag(UserNotFound)(lambda e: succeed(f"Who is {e.user_id}?")),
    provide(layer.succeed(Database, InMemoryDatabase({"u1": "Alice"}))),
)

run_sync(program)  # type error here if any error is unhandled or service unprovided

The two guarantees, enforced by pyright at the run_sync boundary:

  • No unhandled errors: every typed error must be caught (or explicitly converted to a defect) before an effect can run.
  • No missing dependencies: every service an effect uses must be provided by a layer.
  • Interface/impl split for free: services are Protocol interfaces, so live and test implementations swap in without subclassing or @runtime_checkable — plain classes work as tags too (tag == impl) when you don't need the split.

See examples/quickstart.py for a runnable version. Design notes and the validated feasibility experiments behind the typing patterns live in PLAN.md and research/experiments/.

Install

pip install effect-python          # or: uv add effect-python
pip install 'effect-python[otel]'  # optional: OpenTelemetry tracing bridge
pip install 'effect-python[http]'  # optional: httpx-backed HTTP client transport

Requires Python ≥ 3.13. The package ships a py.typed marker, so pyright sees its types out of the box.

Documentation

Task-focused guides, each mirroring an Effect docs chapter with Python examples sourced from the test suite:

Each guide ends with a "Deviations from Effect-TS" section explaining where Python's type system or runtime forces a different spelling.

Observability

@fn and with_span emit tracing spans through a Tracer default service — zero overhead until you provide one. provide(layer.succeed(Tracer, ...)) installs any backend; effect_py.otel (the [otel] extra) bridges to OpenTelemetry:

from effect_py import provide, run_sync, with_span
from effect_py import otel  # requires: pip install 'effect-python[otel]'

program.pipe(with_span("handle-request")).pipe(provide(otel.layer()))

See basics — instrumentation.

Status

Alpha (0.1.0a, M1–M8 of the plan complete): core effects, generators (gen/fn), typed errors with catch_tag subtraction, services, layers with memoization (including the dependency-hiding layer_provide), the asyncio runtime, schedules/retry/timeout, fibers, the TestClock + pytest integration, schema, config, tracing (with the optional OpenTelemetry bridge), the HTTP client family (with the optional httpx transport), and the CLI package (typed CliConfig params, subcommands, global flags, the Console default service). See PLAN.md for the full milestone history.

Requirements

  • Python ≥ 3.13 (PEP 695 type parameters with PEP 696 defaults)
  • pyright for type checking (ty cannot yet infer the core patterns; mypy is not supported — it rejects passing a Protocol class where type[T] is expected, which Protocol service tags rely on); see Pyright strict: the one carve-out for the one inference gap every consumer hits

Pyright strict: the one carve-out

The library's pitch is full E/R/A inference from unannotated generator functions. The one hole: pyright cannot infer a generator's send type through yield from — it treats the send channel as a contravariant input (like a parameter, hence the rule name) and infers Unknown. TypeScript propagates send types through yield*, which is why Effect-TS needs no equivalent carve-out.

Symptom, under typeCheckingMode = "strict": every unannotated effect generator's def line errors with reportUnknownParameterType:

Return type "Generator[Effect[...], Unknown, ...]" is partially unknown

The Unknown is def-site noise only — gen/@fn still solve the returned Effect[A, E, R] exactly, and it never leaks into consuming code.

Asked upstream, declined: microsoft/pyright#10279 — closed as not planned, though the maintainer will revisit given upvote signal. Upvotes welcome.

Three postures, ranked:

  1. Scope the rule off for effect-heavy directories via executionEnvironments — the rest of the project stays fully strict:

    [tool.pyright]
    typeCheckingMode = "strict"
    executionEnvironments = [
        { root = "src/myapp", reportUnknownParameterType = "none" },
    ]
    
  2. Per-file: add # pyright: reportUnknownParameterType=false as the first line of files that define effect generators. Zero config; travels with the file.

  3. Zero carve-out: annotate generators with the exported EffectGen[A, E = Never, R = Never] alias. Explicit E/R/A instead of inferred — but checked, not trusted: under-declaring E or getting A wrong is a type error at the def site.

    from effect_py import EffectGen, fn, service
    
    @fn("Users.find")
    def find(user_id: str) -> EffectGen[str, UserNotFound, Database]:
        db = yield from service(Database)
        ...
    

gen(lambda: f(x)) additionally trips reportUnknownLambdaType; use @fn for parameterized generators (the library already steers this way).

This repo dogfoods posture 1 — the carve-out is scoped to tests and examples; src/effect_py itself passes full strict with zero suppressions. tests/typecheck/cases/send_type_hole.py pins the pyright behavior: if pyright ever fixes the inference, that test fails and this section can be deleted.

Development

uv sync
uv run pytest
uv run pyright
uv run ruff check

tests/typecheck/ pins pyright's inference behavior — the library's core guarantees live in the type checker, so inference regressions fail CI like any other bug.

Metadata

Release files for effect-python 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for effect-python 0.1.0a2
File Size Uploaded Provenance Actions
effect_python-0.1.0a2.tar.gz 110.0 kB With provenance Details for effect_python-0.1.0a2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for effect-python 0.1.0a2
File Interpreter ABI Platform Actions
effect_python-0.1.0a2-py3-none-any.whl Python 3 none any Details for effect_python-0.1.0a2-py3-none-any.whl

Total release size: 242.9 kB

Release files / effect_python-0.1.0a2.tar.gz

Download URL effect_python-0.1.0a2.tar.gz
Size 110.0 kB
Tags Source
SHA-256 checksum
How to use checksums
c83146d4cfed1afb75ef44ecfab6f191b2ce1346c0c9d6e0ee6073f50cf0d307
BLAKE2b-256 checksum
How to use checksums
57502463602ea64d5604bd955fc5123dd01bd646dfa5b53bb254805dc24a60ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 11, 2026.

Transparency log

Release files / effect_python-0.1.0a2-py3-none-any.whl

Download URL effect_python-0.1.0a2-py3-none-any.whl
Size 132.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4bb603a90dd16c19af7c5b3c917ca729199059920e8270c298d9edb53c3a4844
BLAKE2b-256 checksum
How to use checksums
5dd158d34d6c4fed42779b2249d15cb83f24b0b46c0e2fcc49abede1ac5a0348
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 11, 2026.

Transparency log

Security for effect-python 0.1.0a2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for effect_python-0.1.0a2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 11, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page