For the complete documentation index, see llms.txt. This page is also available as Markdown.

Python SDK

Integrate the Harness FME Python SDK into your server-side applications to manage feature flags and user treatments.

This guide provides detailed information about our Python SDK. All of our SDKs are open source. Go to our Python SDK GitHub repository to learn more.

Before you begin

The Python SDK supports Python 3 (3.7.16 or later).

RULE-BASED SEGMENTS SUPPORT

Rule-based segments are supported in SDK versions 10.4.0 and above. No changes are required to your SDK implementation, but updating to a supported version is required to ensure compatibility.

Older SDK versions will return the control treatment for flags using rule-based segments and log an impression with a special label for unsupported targeting rules.

Multi-thread, multi-process and asyncio modes support

One of Python's great built-in features is the ability to parallelize your code to optimize the execution-performance of any module. You can implement your project in a multi-threaded, multi-process or asyncio mode, depending on what works best for you and your team.

Since multiple processes in Python are unable to share memory space, the setup and instantiation process is different for each mode. Django projects are multi-process by default.

Jump to the setup process for the mode your application is built in:

Initialization: Multi-threaded mode

Set up FME in your code base with two simple steps.

1. Import the SDK into your project using pip

2. Instantiate the SDK and create a new SDK factory client

When the SDK is instantiated in in-memory mode, it kicks off background tasks to update an in-memory cache with small amounts of data fetched from Harness servers. This process can take up to a few hundred milliseconds depending on the size of data. If the SDK is asked to evaluate which treatment to show to a customer for a specific feature flag while its in this intermediate state, it may not have the data necessary to run the evaluation. In this case, the SDK doesn't fail, rather, it returns the control treatment.

To make sure the SDK is properly loaded before asking it for a treatment, block until the SDK is ready. Since version 8.0.0 This is done by calling the .block_until_ready() method in the factory object. This method also accepts a maximum time (in seconds or fractions of it) to wait until the SDK is ready, or throw an exception in case it's not.

We recommend instantiating the SDK factory once as a singleton and reusing it throughout your application.

Configure the SDK with the SDK key for the FME environment that you would like to access. In legacy Split (app.split.io) the SDK key is found on your Admin settings page, in the API keys section. Select a server-side SDK API key. See API keys to learn more.

Now you can start asking the SDK to evaluate treatments for your customers.

Initialization: asyncio mode

Python's asyncio library had gather lot of attention and support and provides many advantages to multi-threaded programming especially in I/O operations, checkout the official doc for more info.

Set up FME in your code base with two simple steps.

1. Import the SDK into your project using pip

2. Instantiate the SDK and create a new SDK factory client

ASYNCIO SUPPORT

When using the SDK, regardless if the mode is asyncio or Multi-threaded, all the public SDK API are identical, with only one exception; when initializing the factory.

Similar to Multi-threaded mode, when the SDK is instantiated in in-memory, it kicks off background asyncio tasks to update an in-memory cache with small amounts of data fetched from Harness servers. To make sure the SDK cache is properly loaded before asking it for a treatment, utilize block_until_ready() method.

We recommend instantiating the SDK once as a singleton and reusing it throughout your application.

Use the code snippet below and plug in your API key. The API key is available on your Organization Settings page, on the APIs tab. The API key is of type sdk. For more information, see Understanding API Keys.

For the following sections, lookup the asyncio tab in each code example block.

Initialization: Multi-process mode

There are a few extra steps for setting up our SDK with Python in multi-process mode, described below. Before hopping into the details, we will quickly review the multi-process mode setup differences.

SDK architecture

When the application is run in a server that spawns multiple processes (workers) to handle HTTP requests, all of them need to access fetched feature flags and segments as well as queuing up impressions and events. Since processes cannot access each other's memory, using the standalone operation mode will result in several sets of synchronisation tasks (threads) doing the same job (at least one per http worker - possibly more, since workers are often restarted). To avoid this scenario, the Split.IO SDK for Python supports an alternative operation mode, which uses an external tool called Split-Synchronizer and a redis cache. Our synchronization tool is responsible for maintaining the FME data updated and flushing impressions, events and metrics to the split servers. If you are using a preforked-type server such as uWSGI or GUnicorn, we also offer a series of methods that can be attached to the server's "post-fork" hooks in order to ensure synchronization runs properly on the worker process after the master is forked.

The previously mentioned approaches are described in depth below:

Redis cache and client setup

Before you get started with the cache, download the correct version of Redis to your machine. Our SDK Redis integration requires a Redis version 2.10.5 or later. Also want to make sure to start your Redis server. Refer to the Redis documentation for help. After that, there are a few more steps to set up the cache with Redis.

1. Install the SDK into your project

Use pip install to install the SDK. Note that the package is different for standard Python and for Django, as shown below.

2. Set up the Split Synchronizer

Set up the Split Synchronizer to sync data to a Redis cache. Follow the steps in the set up article, then come back to this doc and go to step 3 to instantiate the client, below.

3. Instantiate the SDK factory client with Redis enabled

If you are using Django, there is one extra step to add django_splitio to INSTALLED_APPS in your Django settings and add a SPLITIO dictionary in the Django settings. Input your own SDK key in for YOUR_SDK_KEY.

To instantiate the SDK factory client, copy and paste the code snippet below into your code base where you want to use Harness FME to roll out your feature flag. Again, note that the syntax is different for standard Python and for Django.

Now you can start asking the SDK to evaluate treatments for your customers.

Redis Sentinel

The SDK also supports Redis with Sentinel (v2) replication. The client can be configured to operate with single master/multiple slaves to provide high availability. The current version of Sentinel is 2. A stable release of Sentinel has been shipped since Redis 2.8. For further information about Sentinel, refer to the Sentinel documentation.

Use the following configuration for Redis in Sentinel mode.

Variable

Type

Description

redisSentinels

array

The list of sentinels for replication service.

redisMasterService

string

The name of master service.

Redis Cluster

This functionality is currently not supported for this SDK, but is planned for a future release. Subscribe to our release notes for updates.

Preforked client setup

Since version 8.4.0 we added support for running our SDK in standalone mode in preforked multiprocess servers. With this feature you can take advantage of using FME in preforking servers such as GUnicorn or uWSGI and attaching it to the postfork hooks. This can yield significant performance improvements in terms of memory in comparison to use lazy-style initialization and greatly reduced evaluation time in comparison to use Redis + Split Synchronizer approach at the expense of CPU and BG network traffic. There are two main steps for initializing the SDK by using hooks:

  1. preforkedInitialization: this is a new configuration option that will tell the SDK that it should initiate the SDK in master mode and it will not start polling nor streaming.

  2. factory.resume(): this is a new method provided by the factory that should be executed on newly forked http worker processes in order to resume synchronisation.

Example using uWSGI preforked server

Adding postfork handler

There are a few extra steps to set up SDK with postfork option.

  1. Importing the uwsgidecorators module for handling hooks.

  2. Set preforkedInitialization as true in the sdk configs.

  3. Add and use the postfork decorator.

  4. Call factory.resume() method to resume tasks on each forked child process.

Note: Make sure to add the parameter --enable-threads to enable multi-threading when starting the UWSGI app server. While Python SDK does support UWSGI app server in process based mode, for the SDK to synchronize with Harness FME servers, you need to enable the multi-threading option, as the background threads perform the syncing task. For example:

For further reading about uwsgi decorators and postfork you can take a look at the official documentation

Use the SDK

Basic use

After you instantiate the SDK factory client, you can start using the get_treatment method of the SDK factory client to decide what version of your feature flags your customers are served. The method requires the FEATURE_FLAG_NAME attribute that you want to ask for a treatment and a unique key attribute that corresponds to the end user that you want to serve the feature to.

From there, you simply need to use an if-else-if block as shown below and insert the code for the different treatments that you defined in Harness FME. Remember the final else branch in your code to handle the client returning the control treatment.

KEY SHOULD BE STRING

If the key attribute is something other than string, Python SDK returns CONTROL after evaluation.

Attribute syntax

To target based on custom attributes, the SDK's get_treatment method needs to be passed an attribute map at runtime.

In the example below, we are rolling out a feature flag to users. The provided attributes plan_type, registered_date, permissions, paying_customer, and deal_size are passed to the get_treatment call. These attributes are compared and evaluated against the attributes used in the rollout plan as defined in Harness FME to decide whether to show the on or off treatment to this account.

The get_treatment method supports five types of attributes: strings, numbers, dates, booleans, and sets. The proper data type and syntax for each are:

  • Strings: Use type String.

  • Numbers: Use type Integer.

  • **Dates: ** Express the value as the number of seconds since the epoch as seconds in UTC. For example, attribute registered_date is arrow.utcnow().timestamp, which is an integer.

  • Booleans: Use type Boolean.

  • Sets: Use type Set.

Multiple evaluations at once

In some instances, you may want to evaluate treatments for multiple feature flag at once. Use the different variations of get_treatments method from the SDK factory client to do this.

  • get_treatments': Pass a list of the feature flag names you want treatments for.

  • get_treatments_by_flag_set: Evaluate all flags that are part of the provided set name and are cached on the SDK instance.

  • get_treatments_by_flag_sets: Evaluate all flags that are part of the provided set names and are cached on the SDK instance.

Multi-threaded

asyncio

Get treatments with configurations

To leverage dynamic configurations with your treatments, you should use the get_treatment_with_config method.

This method will return an object containing the treatment and associated configuration.

The config element will be a stringified version of the configuration JSON defined in Harness FME. If there are no configs defined for a treatment, the SDK returns None for the config parameter.

This method takes the exact same set of arguments as the standard get_treatment method. See below for examples on proper usage:

If you need to get multiple evaluations at once, you can also use the get_treatments_with_config methods. These methods take the exact same arguments as the get_treatments methods but return a mapping of feature flag names to SplitResult objects instead of strings. Example usage below:

Multi-threaded

asyncio

If a flag cannot be evaluated, the SDK returns the fallback treatment value (default "control" unless overridden globally or per flag). For more information, see Fallback treatments.

Append properties to impressions

Impressions are generated by the SDK each time a getTreatment method is called. These impressions are periodically sent back to Harness servers for feature monitoring and experimentation.

You can append properties to an impression by passing an object of key-value pairs to the getTreatment method. These properties are then included in the impression sent by the SDK and can provide useful context to the impression data.

Three types of properties are supported: strings, numbers, and booleans.

Shutdown

The in-memory implementation of Python uses threads in Multi-threaded mode and tasks in asyncio mode to synchronize feature flags, segments, and impressions. If at any point in the application the SDK factory client is not longer needed, you can disable it by calling the destroy() method on the factory object.

This does NOT kill the threads or tasks if they are synchronizing, but prevents them from rescheduling for future executions.

When you call the .destroy() method from the client, any subsequent call to get_treatment()returns CONTROL, and when querying splits or split_names via the manager interface, an empty list [] is returned.

Since version 8.0.0 .destroy() accepts an optional argument of type threading.Event. This allows the user to have control of the shutdown cycle of the SDK. The user can for example choose to block the application until destroy() has finished, so that all the impressions and events are flushed correctly before the application shuts down.

Track

Use the track method to record any actions your customers perform. Each action is known as an event and corresponds to an event type. Calling track through one of our SDKs or via the API is the first step to and allows you to measure the impact of your feature flags on your users’ actions and metrics.

Learn more about using track events in feature flags.

In the examples below you can see that the .track() method can take up to five arguments. The proper data type and syntax for each are:

  • key: The key variable used in the get_treatment call and firing this track event. The expected data type is String.

  • TRAFFIC_TYPE: The traffic type of the key in the track call. The expected data type is String. You can only pass values that match the names of traffic types that you have defined in Harness FME.

  • EVENT_TYPE: The event type that this event should correspond to. The expected data type is String. Full requirements on this argument are:

    • Contains 63 characters or fewer.

    • Starts with a letter or number.

    • Contains only letters, numbers, hyphen, underscore, or period.

    • This is the regular expression we use to validate the value: [a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}

  • VALUE: (Optional) The value to be used in creating the metric. This field can be sent in as null or 0 if you intend to purely use the count function when creating a metric. The expected data type is Integer or Float.

  • PROPERTIES: (Optional) A Map of key value pairs that can be used to filter your metrics. Learn more about event property capture in the Events guide. FME currently supports three types of properties: strings, numbers, and booleans.

Redis Support: If you are using our SDK with Redis, you need Split Synchronizer 2.3.0 version at least in order to support properties in the track method.

The track method returns a boolean value of true or false to indicate whether or not the SDK was able to successfully queue the event to be sent back to Harness servers on the next event post. The SDK will return false if the current queue size is equal to the config set by eventsQueueSize or if an incorrect input to the track method has been provided.

In the case that a bad input has been provided, you can read more about our SDK's expected behavior here.

Configuration

The SDK has a number of knobs for configuring performance. Each knob is tuned to a reasonable default. However, you can override the value while instantiating the SDK. The parameters available for configuration are shown below.

Configuration

Description

Default value

Applies to

featuresRefreshRate

The SDK polls Harness servers for changes to feature flags at this period (in seconds).

30 seconds

In-memory.

segmentsRefreshRate

The SDK polls Harness servers for changes to segments at this period (in seconds).

30 seconds

In-memory.

impressionsRefreshRate

The treatment log captures which customer saw what treatment (on, off, and so on) at what time. This log is periodically flushed back to Harness servers. This configuration controls how quickly the cache expires after a write (in seconds).

300 seconds

In-memory.

metricsRefreshRate

The SDK caches diagnostic data that it periodically sends to Harness servers. This configuration controls how frequently this data is sent back to Harness servers (in seconds).

3600 seconds

In-memory.

eventsPushRate

How often the SDK sends events to the backend.

10 seconds

In-memory.

labelsEnabled

Disable labels from being sent to Harness servers. Labels may contain sensitive information.

true

All operation modes.

connectionTimeout

HTTP client connection timeout (in ms).

1500ms

In-memory.

apiKey

The SDK key. This entry is mandatory. If localhost is supplied as the SDK key, a localhost only client is created when get_client is called.

None

All operation modes.

redisHost

The host that contains the Redis instance.

localhost

Redis-based storage setup.

redisPort

The port of the Redis instance.

6379

Redis-based storage setup.

redisDb

The db index on the Redis instance.

0

Redis-based storage setup.

redisUsername

The user name for Redis.

None

Redis-based storage setup.

redisPassword

The password for Redis.

None

Redis-based storage setup.

redisPrefix

The prefix for each key written in Redis by the SDK.

None

Redis-based storage setup.

redisSsl

Enable encrypted connections to redis.

False

Redis-based storage setup.

redisSslKeyfile

Client key used to decrypt incoming responses.

None

Redis-based storage setup.

redisSslCertfile

Client certificate to prove client's identity to the server.

None

Redis-based storage setup.

redisSslCertReqs

Whether to validate the server public key or blindly accept it and use it.

None

Redis-based storage setup.

redisSslCaCerts

CA Root certificates capable of validating the certificate presented by the server.

None

Redis-based storage setup.

redisLocalCacheEnabled

Enable a local in-memory cache on top of redis for fetching feature flags.

True

Redis-based storage setup.

redisLocalCacheTTL

How long to cache feature flags in memory (in seconds).

5

Redis-based storage setup.

redisSocketTimeout

Socket Timeout for Redis.

None

Redis-based storage setup.

redisSocketConnectTimeout

Socket Connection Timeout for Redis.

None

Redis-based storage setup.

redisRetryOnTimeout

If retries for Redis operations would be performed by the SDK if it receives a timeout.

False

Redis-based storage setup.

preforkedInitialization

Flag for enabling fork execution (requires extra setup mentioned on the preforked client setup section).

False

Preforked running mode.

impressionListener

Custom implementation of impression listener interface.

None

Redis-based storage setup.

eventsQueueSize

Max number of events to accumulate before sending them to the backend.

10000

In-memory.

eventsBulkSize

How many events to package when submitting them to the split servers

5000

In-memory.

impressionsQueueSize

Max number of impressions to accumulate before sending them to the backend.

10000

In-memory.

impressionsBulkSize

How many impressions to package when submitting them to the split servers

5000

In-memory.

IPAddressesEnabled

Flag to disable IP addresses and host name from being sent to the Harness servers.

True

Redis, In-memory.

impressionsMode

This configuration defines how impressions are queued on the SDK. Supported modes are OPTIMIZED, NONE, and DEBUG. In OPTIMIZED mode, only unique impressions are queued and posted to Harness; this is the recommended mode for experimentation use cases. In NONE mode, no impression is tracked in Harness FME and only minimum viable data to support usage stats is, so never use this mode if you are experimenting with that instance impressions. In DEBUG mode, ALL impressions are queued and sent to Harness. Use DEBUG mode when you want every impression to be logged in Harness FME when trying to debug your SDK setup. This setting does not impact the impression listener which will receives all generated impressions.

'optimized'

In-memory operation mode.

streamingEnabled

Boolean flag to enable the streaming service as default synchronization mechanism. In the event of an issue with streaming, the SDK will fallback to the polling mechanism. If false, the SDK will poll for changes as usual without attempting to use streaming.

True

In-memory operation mode.

flagSetsFilter

This setting allows the SDK to only synchronize the feature flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.

None

IMPRESSION LISTENER

Starting with this version, the SDK removed the impression_listener parameter. If this parameter is passed, it is not handled by the SDK. If you want to attach a custom impression listener, send the new impressionListener parameter with an implementation of the impression listener interface.

To set each of the parameters defined above, the syntax should be as seen below.

Note that if you are using Standard Python, you pass the configuration parameters as a dictionary to the factory. In Django, plug the configuration parameters in to the SPLITIO dictionary in your Django settings

Localhost mode

A developer can put code behind feature flags on their development machine without the SDK requiring network connectivity. To achieve this, the SDK can be started in localhost mode (aka off-the-grid mode). In this mode, the SDK neither polls nor updates Harness servers. Instead, it uses an in-memory data structure to determine what treatments to show to the logged in customer for each of the features. To use the SDK in localhost mode, replace the SDK Key with "localhost", as shown in the example below:

With this mode, you can instantiate the SDKS using one of the following methods:

  • JSON: Full support, for advanced cases or replicating an environment by pulling rules from Harness FME servers (from version 9.4.0).

  • YAML: Supports dynamic configs, individual targets, and default rules (from version 8.0.0).

  • .split: Legacy option, only treatment result.

JSON

Since version 9.4.0, our SDK supports localhost mode by using the JSON format. This version allows the user to map feature flags and segment definitions in the same format as the APIs that receive the data. This mode needs the following extra configuration to be set:

Name

Description

Type

splitFile

Indicates the path of the feature flag file location to read

string

segmentDirectory

Indicates the path where all the segment files are located

string

localhostRefreshEnabled

Flag to run synchronization refresh for feature flags and segments in localhost mode.

bool

splitFile

The following splitFile is a JSON that represents a SplitChange:

segmentDirectory

The provided segment directory must have the JSON files of the corresponding segment linked to previous feature flag definitions. According to the Feature flag file sample above: feature_flag_1 has segment_1 linked. That means that the segmentDirectory must have segment_1 definition.

Init example

YAML

Since version 8.0.0, our SDK supports a new type of localhost feature flag definition file, using the YAML format. This new format allows the user to map different keys to different treatments within a single feature flag, and also add configurations to them. The new format is a list of single-key maps (one per mapping feature-flag-keys-config), defined as follows:

In the example above, we have 3 entries:

  • The first entry defines that for feature flag my_feature_flag, the key key will return the treatment on and the on treatment will be tied to the configuration {"desc" : "this applies only to ON treatment"}.

  • The second entry defines that the feature flag some_other_feature_flag will always return the off treatment and no configuration.

  • The third entry defines that my_feature_flag will always return off for all keys that don't match another entry (in this case, any key other than key).

.SPLIT file

In this mode, the SDK loads a mapping of Feature Flag name to treatment from a file at $HOME/.split. For a given feature flag, the treatment specified in the file is returned for every customer. Should you want to use another file, you just need to set the splitFile key in the configuration dictionary passed at instantiation time, to the full path of the desired file.

The following is a sample .split file. The format of this file is two columns separated by a whitespace. The left column is the feature flag name, and the right column is the treatment name.

Manager

Use the Split Manager to get a list of feature flags available to the SDK factory client.

To instantiate a Manager in your code base, use the same factory that you used for your client.

The Manager then has the following methods available.

The SplitView object that you see referenced above has the following structure.

Listener

FME SDKs send impression data back to Harness servers periodically when evaluating feature flags. To send this information to a location of your choice, define and attach an impression listener. Use the SDK's impressionListener parameter, where you can add an implementation of ImpressionListener. This implementation must define the log_impression method. It receives data in the following schema.

Name

Type

Description

impression

impression

Impression object that has the feature name, treatment result, label, etc.

attributes

array

A list of attributes passed by the client.

instance-id

string

The IP address of the machine running the SDK.

sdk-language-version

string

The version of the SDK. In this case the language is python plus the version.

Implement a custom impression listener

Here is an example of how implement a custom impression listener.

Attach a custom impression listener

Here is an example of how to implement a custom impression listener.

Logging

Since version 8.3.0 the loggers use a hierarchical approach, which enable the user to handle all split-sdk related logs either as a whole or as independent components. Each module has it's own logger, the root being splitio. Below is an example of simple usage.

For asyncio mode, since the SDK uses the same logger library, we suggest to create a custom listener to fetch logging lines in a separate thread to avoid any blocking in asyncio tasks during I/O logging operations, see example below:

For older versions, to set a specific location or logging threshold, use the following syntax.

Proxy

You can configure proxies by setting the environment variables HTTP_PROXY and HTTPS_PROXY. The SDK uses those variables to perform the server request.

Configure fallback treatments

Fallback treatments let you define a treatment value (and optional configuration) to be returned when a flag cannot be evaluated. By default, the SDK returns control, but you can override this globally or for individual flags at the SDK level.

This is useful when you want to:

  • Maintain a predictable user experience during outages or evaluation failures (avoid unexpected control in production)

  • Protect critical user flows by returning a safe, stable treatment (for example, forcing off during an incident)

  • Customize behavior per flag so each evaluation inherits appropriate safe defaults if something goes wrong

Global fallback treatment

Set a global fallback treatment when initializing the SDK factory. This value is returned whenever any flag cannot be evaluated.

Flag-level fallback treatment

You can also set a fallback treatment per flag when calling getTreatment or getTreatmentWithConfig. This flag-level fallback always takes precedence over the global fallback treatment, so if both are defined, the SDK will return the flag-level value when that flag cannot be evaluated.

For more information, see Fallback treatments.

Advanced use cases

This section describes advanced use cases and features provided by the SDK.

Subscribe to events

SUPPORTED SDK VERSIONS

SDK events and event metadata are supported in the Python SDK version 10.6.0 or later.

You can listen for four different events from the SDK.

  • SDK_READY. This event fires once the SDK is ready to evaluate treatments using the most up-to-date version of your rollout plan, downloaded from Harness servers.

  • SDK_UPDATE. This event fires whenever your rollout plan is changed. Listen for this event to refresh your app whenever a feature flag or segment is changed in Harness FME.

These events provide hooks to run custom logic whenever the SDK state changes.

Include metadata

metadata provides additional context for events:

  • SDK_READY: No metadata is included.

  • SDK_UPDATE: Includes the type (SdkEventType.FLAG_UPDATE or SdkEventType.SEGMENTS_UPDATE) and names (list of impacted flags; empty for segment-only updates).

Troubleshooting

Error: type() argument 1 must be string, not unicode

When initializing the SDK factory object in Python, the following exception occurs:

Full traceback excerpt:

This error happens because the Python SDK requires the enum34 library version 1.1.5 or above. If an older version (e.g., 1.0.x) is installed, or your environment is forcing an older version, this exception will be thrown during SDK initialization.

Upgrade the enum34 package to version 1.1.5 or higher. Run the following command:

As of this writing, the latest version is 1.1.6.

Last updated

Was this helpful?