Toggle on/off features. Rollout python port

pip install hanoi==0.0.4



Version Downloads Build Status

Beta version.

Hanoi is a port of rollout gem from James Golick and Eric Rafaloff.

The idea behind it is to ease a simple way to enable/disable functionalities to a subset of users in a production (or any other) environment. This is in general handy upon deploying a new version of your product, in order to test the new functionalities in a subset of users. It could be useful as well as ACL mechanism.

Use cases

  • Enable a functionality globally (for every user using the system).
  • Enable a functionality to a percentage of users via Cyclic Redundancy Check(user identifier) % 100.
  • Enable a functionality to a percentage of users via a predefined rule using a Regular Expression.
  • Enable a functionality to specific user identifiers.
  • Variants support (new in 0.0.4): inspired in feature by Esty and sixpack, hanoi now supports variant for providing to users different options for an experiment.


  • Check if the functionality A is enabled for user B.
rollout.is_enabled('A', B)
  • Pre-check while executing a function (functionality A) that user B (received as parameter in the function call) is granted permissions.
@roll.check('A', 1)  # Check if user B (argument 1) is granted permissions to execute A
def execute_a_logic(user):
    pass  # Business logic here

  • Pre-check to ensure user B (attached to the process/thread) can execute functionality A (ACL mechanism). Be aware if your environment requires thread-safe behavior.

@roll.check('A')  # Check if the current user (B) is granted permissions to execute A
def execute_a_logic():
    pass  # Business logic here

  • Retrieving a valid variant for an experiment A for user B
variant = rollout.variant('A', B)

Examples of usage

# Setting the configuration
# -------------------------

# bootstrap.py

import re

import hanoi

rollout = hanoi.Rollout(hanoi.RedisHighPerfBackEnd())

    'cdc_on',               # Functionality name (CDC on)
    percentage=80,          # Percentage for toggle ON
    variants=('foo', 'bar') # Valid variants in case of toggle ON

rollout.register('cdc_on', '447568110000')  # Register a specific user

rollout.register('cdc_on', re.compile(r'01$')  # Register a subset of users

def get_rollout():
    return rollout

# Using Rollout
# -------------

# service.py

import bootstrap

roll = bootstrap.get_rollout()

# Define the current user (kind of ThreadLocal)

@roll.check('cdc_on')  # Check if the current user is registerd to `cdc_on`
def execute_cdc_logic():

# Based on the rules defined in bootstrap.py,
# the decorator will not allow the function execution,
# as zlib.crc32('444401') % 100 = 89, and the predefined percentage is 80

# Check if it's enabled `cdc_on` to the user `44488`
# Based on the rules defined in bootstrap.py, it will return False
print roll.is_enabled('cdc_on', '44488')

# Check if it's enabled `cdc_on` to the second parameter
@roll.check('cdc_on', 2)
def execute_again_cdc_logic(parameter, user):
    return "I'm in"

# Based on the rules defined in bootstrap.py,
# the decorator will allow the function execution, as 443301 matches the reg expr.
print execute_again_cdc_logic('foo', '443301')

# Get a valid variant for user `443301`
print roll.variant('cdc_on', '443301')


Currently there're three implemented BackEnds:

  • MemoryBackEnd: useful for development or where you have predefined rules and don't need to share information between different processes.

  • RedisBackEnd: useful for distributed environments, where you need to easily update functionalities, rules or users attached to a specific functionality.

  • RedisHighPerfBackEnd: useful for distributed environments and high performance. It uses SET to store users and reduce significantly the time to verify an user. Check and execute benchmark.py file for details.

TODO before BETA

  • Finish unit testing Rollout class
  • Finish unit testing Function class
  • Implement Redis BackEnd
  • Finish unit testing RedisBackEnd class
  • Document the different use cases and when to use both backends
  • Integrate travis.ci
  • Upload a beta version to pypi
  • Think about a cooler name 😉. We'll stay with hanoi
  • Write a blog post