Skip to main content

Feature toggles for Python, decorate a function with @toggle and it becomes gated by its own name

Project description

FtrIO (Python)

Feature toggles for Python, ported faithfully from the .NET FtrIO library. Decorate a method, add a key to appsettings.json, and the method runs only when the toggle is on. Richer decisions, percentage rollouts, A/B buckets, deployment slots, user and attribute targeting, per-user overrides, are layered in through a strategy chain.

The core depends on the standard library only.

Installation

pip install ftrio

The core has no third-party dependencies. The Azure App Configuration provider needs an optional extra:

pip install "ftrio[azure]"

(Contributors working on FtrIO itself want the editable dev install instead; see Development.)

Quickstart

  1. Decorate a method. The toggle key defaults to the method's own name.

    from ftrio import toggle
    
    @toggle
    def send_welcome_email(user):
        ...  # runs only when the "send_welcome_email" toggle is on
    
  2. Add a matching key to appsettings.json in your working directory:

    {
        "Toggles": {
            "send_welcome_email": true
        }
    }
    
  3. Call it normally. When the toggle is off, the call returns None without running the body (an async @toggle_async returns an awaitable resolving to None, so await is always safe).

If there is no appsettings.json on disk at all, every toggle reads on, a fresh app stays fully functional before any config exists. A present file with a missing key raises ToggleDoesNotExistError; a present key with an uninterpretable value raises ToggleParsedOutOfRangeError.

The builder pipeline

Plain true/false is the baseline. For richer decisions, build a parser and install it as the ambient parser used by the decorators:

from ftrio import ToggleParserProvider

ToggleParserProvider.configure_builder(lambda builder: builder
    .with_context_strategies(context_accessor)  # user targeting + attributes + A/B
    .with_percentage_rollout()                   # "20%"
    .with_blue_green()                           # "blue" / "green" from appsettings.json
    .with_overrides())                           # per-user TogglesOverrides, checked first

Strategies are tried in registration order; the first whose can_handle accepts the raw value owns the decision. BooleanStrategy is always appended last, so plain booleans keep working under any chain. Toggle value grammars:

Value Strategy Meaning
true / false / 1 / 0 Boolean plain on/off
20% PercentageRollout on for ~20% of calls (random per call)
blue / green BlueGreen on when it names the active deployment slot
users:alice,bob UserTargeting on for the listed user ids
attribute:plan equals premium AttributeRule on when the rule matches the user's attribute
ab:50 or ab:50:salt ABTest deterministic per-user 50% bucket

A/B bucketing is stable: the same user, key (and salt) always bucket identically, and identically to the .NET implementation (SHA-256, first four bytes as a little-endian signed int, absolute value modulo 100).

Per-user overrides (TogglesOverrides) win unconditionally, before any strategy:

{
    "Toggles": { "NewCheckout": "ab:50" },
    "TogglesOverrides": { "NewCheckout": { "alice": true } }
}

Providers and the buffer model

External sources feed a ToggleProviderBuffer, which flushes staged values to appsettings.json atomically on an interval. appsettings.json stays the on-disk source of truth, so reads survive a provider going offline (fail-safe).

from ftrio import ToggleProviderBuffer
from ftrio.providers import HttpToggleParser

buffer = ToggleProviderBuffer()
HttpToggleParser("https://flags.example.com/toggles", buffer)  # polls, stages, flushes

Available providers: HttpToggleParser (standard library), EnvironmentVariableToggleParser (standalone or buffer mode), and AzureAppConfigToggleParser (needs the ftrio[azure] extra). Each exposes close() and context-manager support.

CompositeToggleParser chains parsers with first-wins fallthrough, e.g. env-var overrides, then a remote provider, then appsettings.json as the durable fallback.

The ftrio lint CLI

The .NET library ships a Roslyn analyzer (diagnostic FTRIO001) that fails the build when a [Toggle]-decorated method has no matching key in appsettings.json. The Python equivalent is a CLI you can run in CI:

$ ftrio lint path/to/project
path/to/project/mod.py:8: FTRIO001: Function 'MissingOne' is decorated with @toggle but has no entry in the Toggles section of appsettings.json

1 toggle(s) missing from appsettings.json.

It walks the tree with ast, resolves each @toggle / @toggle_async key, and exits non-zero on findings so it can gate a pipeline.

Non-project directories (.venv, .git, build, dist, __pycache__, and the usual tool caches) are skipped by default, so it never descends into installed dependencies. Skip additional paths with --exclude (repeatable, and also accepts a comma-separated list); patterns are globs matched against each path component and the relative path:

$ ftrio lint . --exclude tests --exclude "*_generated.py"
$ ftrio lint . --exclude tests,scripts

Use --no-default-excludes to scan everything, and -v/--verbose to see what is being scanned. Test fixtures that create their config dynamically are a common case for --exclude tests.

Configuration

appsettings.json keeps the .NET section names (Toggles, TogglesOverrides, FtrIO) for cross-language and wire compatibility, the HTTP provider returns this exact shape. Notable FtrIO settings: ReloadOnChange (re-read on each lookup so live edits apply without a restart), FlushInterval, Environment (overlays appsettings.{Environment}.json), and BlueGreen:CurrentSlot / KnownSlots.

The active environment resolves from FtrIO:Environment, then ASPNETCORE_ENVIRONMENT, then DOTNET_ENVIRONMENT (with FTRIO_ENVIRONMENT as an additive Python-native alias).

Playground

$ python -m playground

Cycles four users every two seconds and prints each toggle's ON/OFF state, honouring live edits to playground/appsettings.json.

Development

$ pip install -e ".[dev]"
$ pytest --cov=ftrio
$ ruff check ftrio
$ mypy ftrio

See PORTING_NOTES.md for every deviation from a literal 1:1 port of the .NET source.

Releasing and changelog

Releases are published to PyPI from a GitHub Release via Trusted Publishing; the step-by-step checklist is in RELEASING.md. Notable changes are recorded in CHANGELOG.md.

Project details


Download files

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

Source Distribution

ftrio-1.0.0.tar.gz (35.8 kB view details)

Uploaded Source

Built Distribution

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

ftrio-1.0.0-py3-none-any.whl (44.5 kB view details)

Uploaded Python 3

File details

Details for the file ftrio-1.0.0.tar.gz.

File metadata

  • Download URL: ftrio-1.0.0.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ftrio-1.0.0.tar.gz
Algorithm Hash digest
SHA256 fe2668534ec3a9fc0f7c67e663b115a3be65481601181e247b0e4854139b3676
MD5 3ef0d6cf2fa733ae73a213a3ee2f3632
BLAKE2b-256 73ecf4376662701777bb45f2fc612693fee0ea287a976b468d7a04e894c783ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for ftrio-1.0.0.tar.gz:

Publisher: publish.yml on FtrOnOff/ftrio-python

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

File details

Details for the file ftrio-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: ftrio-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 44.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ftrio-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b332a03851ec2c11a653b8dc9771c64b1438d961d11e65d3764f86b88b40ee90
MD5 ffa3cf9bfd47fa322547362713da2104
BLAKE2b-256 9e2557001e4579469d680bdcb2eb915691db06a66f04e8ead5c869401ccaab23

See more details on using hashes here.

Provenance

The following attestation bundles were made for ftrio-1.0.0-py3-none-any.whl:

Publisher: publish.yml on FtrOnOff/ftrio-python

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

Supported by

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