Skip to main content

Powerful Feature flagging and A/B testing for Python apps

Project description

GrowthBook Python SDK

Powerful Feature flagging and A/B testing for Python apps.

Build Status

  • No external dependencies
  • Lightweight and fast
  • No HTTP requests everything is defined and evaluated locally
  • Python 3.6+
  • 100% test coverage
  • Flexible targeting
  • Use your existing event tracking (GA, Segment, Mixpanel, custom)
  • Remote configuration to change feature flags without deploying new code

Installation

pip install growthbook (recommended) or copy growthbook.py into your project

Quick Usage

import requests
from growthbook import GrowthBook


# We recommend using a db or cache layer in production
apiResp = requests.get("https://cdn.growthbook.io/api/features/MY_API_KEY")
features = apiResp.json()["features"]

# User attributes for targeting and experimentation
attributes = {
  "id": "123",
  "customUserAttribute": "foo"
}

def on_experiment_viewed(experiment, result):
  # Use whatever event tracking system you want
  analytics.track(attributes["id"], "Experiment Viewed", {
    'experimentId': experiment.key,
    'variationId': result.variationId
  })

# Create a GrowthBook instance
gb = GrowthBook(
  attributes = attributes,
  features = features,
  trackingCallback = on_experiment_viewed
)

# Simple on/off feature gating
if gb.isOn("my-feature"):
  print("My feature is on!")

# Get the value of a feature with a fallback
color = gb.getFeatureValue("button-color-feature", "blue")

GrowthBook class

The GrowthBook constructor has the following parameters:

  • enabled (bool) - Flag to globally disable all experiments. Default true.
  • attributes (dict) - Dictionary of user attributes that are used for targeting and to assign variations
  • url (str) - The URL of the current request (if applicable)
  • features (dict) - Feature definitions from the GrowthBook API
  • forcedVariations (dict) - Dictionary of forced experiment variations (used for QA)
  • qaMode (boolean) - If true, random assignment is disabled and only explicitly forced variations are used.
  • trackingCallback (callable) - A function that takes experiment and result as arguments.

There are also getter and setter methods for features and attributes if you need to update them later in the request:

gb.setFeatures(gb.getFeatures())
gb.setAttributes(gb.getAttributes())

Features

Defines all of the available features plus rules for how to assign values to users. Features are usually fetched from the GrowthBook API and persisted in cache or a database in production.

Feature definitions are defined in a JSON format. You can fetch them directly from the GrowthBook API:

import requests

apiResp = requests.get("https://cdn.growthbook.io/api/features/MY_API_KEY")
features = apiResp.json()["features"]

Or, you can use a copy stored in your database or cache server instead:

import json

json_string = '{"feature-1":{...},"feature-2":{...},...}'
features = json.loads(json_string)

We recommend using the db/cache approach for production.

Attributes

You can specify attributes about the current user and request. These are used for two things:

  1. Feature targeting (e.g. paid users get one value, free users get another)
  2. Assigning persistent variations in A/B tests (e.g. user id "123" always gets variation B)

Attributes can be any JSON data type - boolean, integer, float, string, list, or dict.

attributes = {
  'id': "123",
  'loggedIn': True,
  'age': 21.5,
  'tags': ["tag1", "tag2"],
  'account': {
    'age': 90
  ]
}

Tracking Experiments

Any time an experiment is run to determine the value of a feature, you want to track that event in your analytics system.

You can use the trackingCallback option to do this:

from growthbook import GrowthBook, Experiment, Result

def on_experiment_viewed(experiment: Experiment, result: Result):
  # Use whatever event tracking system you want
  analytics.track(attributes["id"], "Experiment Viewed", {
    'experimentId': experiment.key,
    'variationId': result.variationId
  })

gb = GrowthBook(
  trackingCallback = on_experiment_viewed
)

Using Features

There are 3 main methods for interacting with features.

  • gb.isOn("feature-key") returns true if the feature is on
  • gb.isOff("feature-key") returns false if the feature is on
  • gb.getFeatureValue("feature-key", "default") returns the value of the feature with a fallback

In addition, you can use gb.evalFeature("feature-key") to get back a FeatureResult object with the following properties:

  • value - The JSON-decoded value of the feature (or null if not defined)
  • on and off - The JSON-decoded value cast to booleans
  • source - Why the value was assigned to the user. One of unknownFeature, defaultValue, force, or experiment
  • experiment - Information about the experiment (if any) which was used to assign the value to the user
  • experimentResult - The result of the experiment (if any) which was used to assign the value to the user

Inline Experiments

Instead of declaring all features up-front and referencing them by ids in your code, you can also just run an experiment directly. This is done with the gb->run method:

from growthbook import Experiment

exp = Experiment(
  key = "my-experiment", 
  variations = ["red", "blue", "green"]
)

# Either "red", "blue", or "green"
print(gb.run(exp).value)

As you can see, there are 2 required parameters for experiments, a string key, and an array of variations. Variations can be any data type, not just strings.

There are a number of additional settings to control the experiment behavior:

  • key (str) - The globally unique tracking key for the experiment
  • variations (any[]) - The different variations to choose between
  • weights (float[]) - How to weight traffic between variations. Must add to 1.
  • coverage (float) - What percent of users should be included in the experiment (between 0 and 1, inclusive)
  • condition (dict) - Targeting conditions
  • force (int) - All users included in the experiment will be forced into the specified variation index
  • hashAttribute (string) - What user attribute should be used to assign variations (defaults to "id")
  • namespace (tuple[str,float,float]) - Used to run mutually exclusive experiments.

Here's an example that uses all of them:

exp = Experiment(
  key="my-test",
  # Variations can be a list of any data type
  variations=[0, 1],
  # Run a 40/60 experiment instead of the default even split (50/50)
  weights=[0.4, 0.6],
  # Only include 20% of users in the experiment
  coverage=0.2,
  # Targeting condition using a MongoDB-like syntax
  condition={
    'country': 'US',
    'browser': {
      '$in': ['chrome', 'firefox']
    }
  },
  # Use an alternate attribute for assigning variations (default is 'id')
  hashAttribute="sessionId",
  # Includes the first 50% of users in the "pricing" namespace
  # Another experiment with a non-overlapping range will be mutually exclusive (e.g. [0.5, 1])
  namespace=("pricing", 0, 0.5),
)

Inline Experiment Return Value

A call to run returns a Result object with a few useful properties:

result = gb.run(exp)

# If user is part of the experiment
print(result.inExperiment) # True or False

# The index of the assigned variation
print(result.variationId) # e.g. 0 or 1

# The value of the assigned variation
print(result.value) # e.g. "A" or "B"

# The user attribute used to assign a variation
print(result.hashAttribute) # "id"

# The value of that attribute
print(result.hashValue) # e.g. "123"

The inExperiment flag is only set to True if the user was randomly assigned a variation. If the user failed any targeting rules or was forced into a specific variation, this flag will be false.

Example Experiments

3-way experiment with uneven variation weights:

gb.run(Experiment(
  key = "3-way-uneven",
  variations = ["A","B","C"],
  weights = [0.5, 0.25, 0.25]
))

Slow rollout (10% of users who match the targeting condition):

# User is marked as being in "qa" and "beta"
gb = GrowthBook(
  attributes = {
    "id": "123",
    "beta": True,
    "qa": True,
  },
)

gb.run(Experiment(
  key = "slow-rollout",
  variations = ["A", "B"],
  coverage = 0.1,
  condition = {
    'beta': True
  }
))

Complex variations

result = gb.run(Experiment(
  key = "complex-variations",
  variations = [
    ("blue", "large"),
    ("green", "small")
  ],
))

# Either "blue,large" OR "green,small"
print(result.value[0] + "," + result.value[1])

Assign variations based on something other than user id

gb = GrowthBook(
  attributes = {
    "id": "123",
    "company": "growthbook"
  }
)

# Users in the same company will always get the same variation
gb.run(Experiment(
  key = "by-company-id",
  variations = ["A", "B"],
  hashAttribute = "company"
))

Django

For Django (and other web frameworks), we recommend adding a simple middleware where you instantiate the GrowthBook object

from growthbook import GrowthBook

def growthbook_middleware(get_response):
    def middleware(request):
        request.gb = GrowthBook(
          # ...
        )
        response = get_response(request)

        request.gb.destroy() # Cleanup

        return response
    return middleware

Then, you can easily evaluate a feature (or run an inline experiment) in any of your views:

def index(request):
    featureEnabled = request.gb.isOn("my-feature")
    # ...

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

growthbook-0.2.0.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

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

growthbook-0.2.0-py2.py3-none-any.whl (15.2 kB view details)

Uploaded Python 2Python 3

File details

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

File metadata

  • Download URL: growthbook-0.2.0.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.6.1 requests/2.24.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

File hashes

Hashes for growthbook-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f8fea32cbdbe43da12b5d596e69cabfbe27042a6b6d29bbfbe7227f2846dedf2
MD5 b075d9c40290b3d414b1ffefec54271e
BLAKE2b-256 e5e7a6c5658ddca8607e3df008c0637f1a48fab0e14d43c3a4f6004fc8776c3c

See more details on using hashes here.

File details

Details for the file growthbook-0.2.0-py2.py3-none-any.whl.

File metadata

  • Download URL: growthbook-0.2.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.6.1 requests/2.24.0 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.8.5

File hashes

Hashes for growthbook-0.2.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 c3367f70dbff658c926694404b3b6a9e0da7a629d661780c58c85e80b6bd1812
MD5 4a075d625e6accd68659e23cb0e959af
BLAKE2b-256 a590aa958faceb4069424e2a811d0ed09772a8aa9665dfd8422b924d2a5c1dab

See more details on using hashes here.

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