Skip to main content

fancy-features

Headless feature management and metered-resource gating for Python. Feature flags, feature groups, per-plan quotas and billing-period usage — with no web framework, no ORM and no runtime dependencies at all.

The Python twin of particle-academy/laravel-fms (PHP) and @particle-academy/fancy-features (Node/TypeScript), and the owner of the shared feature contract fancy-catalog consumes.

pip install fancy-features

In one minute

from fancy_features import create_features

features = create_features(
    features={
        "use-mcp": {"enabled": True},
        "ai-tokens": {"type": "resource", "limit": 10_000},
    },
    groups=[
        {"key": "pro", "features": ["sso"], "overrides": {"ai-tokens": {"limit": 250_000}}},
    ],
)

features.can_access("use-mcp", user)  # True
features.remaining("ai-tokens", user)  # 10000
features.try_consume("ai-tokens", user, 40)  # True  -- atomic check-and-increment
features.remaining("ai-tokens", user)  # 9960
features.explain("ai-tokens", user)  # AccessResult(source="config", used=40, ...)

Every method has an a-prefixed twin for async hosts — acan_access, aremaining, atry_consume — and both drive one copy of the resolution rules, so they cannot disagree.

How a verdict is reached

pre-strategies → gate → registry → groups (OR) → config → sources → deny
  • pre-strategies and gate are authoritative: a non-None answer from either is final in both directions. That is what lets a billing service deny something a stray permission would allow.
  • everything after them is additive: a feature defined with enabled=False does not block a group or a plan from turning it on.

For a resource feature:

pre-remaining strategies → MAX(group limit, source limit, feature limit) − usage

clamped at zero, with None meaning unlimited. MAX, because a plan should be able to lift a base limit and never to lower one.

Adapters, all optional

Contract Default What a host plugs in
UsageStore InMemoryUsageStore its feature_usages table
GroupStore InMemoryGroupStore its group-assignment table
FeatureSource none fancy-catalog, or its own entitlement service
gate none its permission system

Every one may be synchronous or asynchronous. A dict-backed store returns an int; a database-backed one returns a coroutine; both work.

Billing periods

from datetime import datetime, UTC
from fancy_features import BillingPeriod

january = BillingPeriod(
    start=datetime(2026, 1, 1, tzinfo=UTC), end=datetime(2026, 2, 1, tzinfo=UTC)
)

features.try_consume("ai-tokens", user, 500, period=january)
features.remaining("ai-tokens", user, period=january)
features.reset_period(user, january)  # the renewal reset

The period reaches the store on every quota path — reads and writes alike.

Composing with a Stripe catalog

from fancy_catalog import create_catalog
from fancy_catalog.features import create_catalog_feature_source
from fancy_features import create_features

catalog = create_catalog(stripe=stripe_client)
features = create_features(
    sources=[create_catalog_feature_source(catalog, resolve_subscription=lookup)],
)

features.can_access("use-mcp", user)  # resolved through the user's plan
features.remaining("ai-tokens", user)  # the plan's included quantity, minus usage

The two packages share one definition of the contract, in fancy_features.contract. The catalog imports it; there is no mirrored copy.

Guarding a route

from fancy_features import FeatureAccessDeniedError, require_feature

try:
    require_feature(features, ["use-mcp", "use-agents"], user)  # OR
except FeatureAccessDeniedError as denied:
    return json_response({"features": denied.features}, status=denied.status)

No framework is imported and none is assumed.

One thing that will bite a porter

Every callback takes (subject, context)check, enabled, limit, usage, remaining. The Node package still publishes usage/remaining as (key, subject, context). A three-parameter callback here raises, naming the feature and the field, rather than binding subject to the key string and quietly metering the wrong thing. Fewer parameters is fine: lambda: 30 works.

Requirements

Python 3.11+. No runtime dependencies.

License

MIT © Particle Academy

Download files

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

Source Distribution

fancy_features-0.2.0.tar.gz (49.6 kB view details)

Uploaded Source

Built Distribution

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

fancy_features-0.2.0-py3-none-any.whl (35.5 kB view details)

Uploaded Python 3

File details

Details for the file fancy_features-0.2.0.tar.gz.

File metadata

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

File hashes

Hashes for fancy_features-0.2.0.tar.gz
Algorithm Hash digest
SHA256 bf58194c659e7e5eba1ca425293fb84f2f8be01f3f5ceedec202050b36c208f4
MD5 0bd22c5a4cdf3af3e2e1b0ea8638997d
BLAKE2b-256 768fee1e8b7b5c304a248c1497ce568857dde0f876f79ae5e307a088ad15176d

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on Particle-Academy/fancy-features-py

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

File details

Details for the file fancy_features-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: fancy_features-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 35.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fancy_features-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d4f6c9b57a2f8a86823bd966aadbfb7af9a0bddd3c4150bdb383210cf066f5cd
MD5 b706221c64c3626e63111ccc45dbb058
BLAKE2b-256 1c2d758377c601a3ca494ed12c754b8de5d886b29390e3b44eba436eda00173c

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on Particle-Academy/fancy-features-py

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 This release

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