No project description provided
Project description
rust_decider
Rust implementation of bucketing, targeting, overrides, and dynamic config logic.
Usage
class Decider
A class used to expose these APIs:
-
choose( feature_name: str, context: Mapping[str, JsonValue] ) -> Decision
-
choose_all( context: Mapping[str, JsonValue], bucketing_field_filter: Optional[str] = None ) -> Dict[str, Decision]
(dynamic configurations)
-
get_bool( feature_name: str, context: Mapping[str, JsonValue], ) -> bool
-
get_int( feature_name: str, context: Mapping[str, JsonValue], ) -> int
-
get_float( feature_name: str, context: Mapping[str, JsonValue], ) -> float
-
get_string( feature_name: str, context: Mapping[str, JsonValue], ) -> str
-
get_map( feature_name: str, context: Mapping[str, JsonValue], ) -> Dict[str, Any]
-
all_values( context: Mapping[str, JsonValue], ) -> Dict[str, Any]
misc:
-
get_feature( feature_name: str, ) -> Feature
choose()
examples:
from rust_decider import Decider
from rust_decider import DeciderException
from rust_decider import FeatureNotFoundException
from rust_decider import DeciderInitException
from rust_decider import PartialLoadException
from rust_decider import ValueTypeMismatchException
# initialize Decider instance
try:
decider = Decider("../cfg.json")
except PartialLoadException as e:
# log errors of misconfigured features
print(f"{e.args[0]}: {e.args[2]}")
# use partially initialized Decider instance
decider = e.args[1]
except DeciderInitException as e:
print(e)
# get a Decision for a feature via choose()
try:
decision = decider.choose(feature_name="exp_1", context={"user_id": "3", "app_name": "ios"})
except DeciderException as e:
print(e)
assert dict(decision) == {
"variant": "variant_0",
"value": None,
"feature_id": 3246,
"feature_name": "exp_1",
"feature_version": 2,
"events": [
"0::::3246::::exp_1::::2::::variant_0::::3::::user_id::::37173982::::2147483648::::test"
]
}
# `user_id` targeting not satisfied so "variant" is `None` in the returned Decision
try:
decision = decider.choose(feature_name="exp_1", context={"user_id": "1"})
except DeciderException as e:
print(e)
assert dict(decision) == {
"variant": None,
"value": None,
"feature_id": 3246,
"feature_name": "exp_1",
"feature_version": 2,
"events": []
}
# handle "feature not found" exception
# (`FeatureNotFoundException` is a subclass of `DeciderException`)
try:
decision = decider.choose(feature_name="not_here", context={"user_id": "1"})
except FeatureNotFoundException as e:
print("handle feature not found exception:")
print(e)
except DeciderException as e:
print(e)
choose_all()
examples:
# `decider` initialized same as above
decisions = decider.choose_all(context={"user_id": "3", "app_name": "ios"}, bucketing_field_filter="user_id")
assert dict(decisions["exp_67"]) == {
"variant": "variant_0",
"value": None,
"feature_id": 3125,
"feature_name": "exp_67",
"feature_version": 4,
"events": [
"0::::3125::::exp_67::::4::::variant_0::::3::::user_id::::37173982::::2147483648::::test"
]
}
Dynamic Configurations + misc. examples:
# `decider` initialized same as above
try:
dc_bool = decider.get_bool("dc_bool", context={})
dc_int = decider.get_int("dc_int", context={})
dc_float = decider.get_float("dc_float", context={})
dc_string = decider.get_string("dc_string", context={})
dc_map = decider.get_map("dc_map", context={})
feature = decider.get_feature("dc_map")
except FeatureNotFoundException as e:
print("handle feature not found exception:")
print(e)
except ValueTypeMismatchException as e:
print("handle type mismatch:")
print(e)
except DeciderException as e:
print(e)
assert dc_bool == True
assert dc_int == 99
assert dc_float == 3.0
assert dc_string == "some_string"
assert dc_map == {
"v": {
"nested_map": {
"w": False,
"x": 1,
"y": "some_string",
"z": 3.0
}
},
"w": False,
"x": 1,
"y": "some_string",
"z": 3.0
}
assert dict(feature) == {
"id": 3393,
"name": "dc_bool",
"version": 2,
"bucket_val": '',
"start_ts": 0,
"stop_ts": 0,
"owner": "test",
"emit_event": False
}
Dynamic Configuration all_values()
example:
# `decider` initialized same as above
decisions = decider.all_values(context={})
assert decisions["dc_int"] == 99
python bindings used in Decider
class
import rust_decider
# Init decider
decider = rust_decider.init("darkmode overrides targeting holdout mutex_group fractional_availability value", "../cfg.json")
# Bucketing needs a context
ctx = rust_decider.make_ctx({"user_id": "7"})
# Get a decision
choice = decider.choose("exp_1", ctx)
assert choice.err() is None # check for errors
choice.decision() # get the variant
# Get a dynamic config value
dc = decider.get_map("dc_map", ctx) # fetch a map DC
assert dc.err() is None # check for errors
dc.val() # get the actual map itself
Development
Updating package with latest src/lib.rs
changes
# In a virtualenv, python >= 3.7
$ cd decider-py
$ pip install -r requirements-dev.txt
$ maturin develop
Running tests
$ pytest decider-py/test/
Publishing
Use conventional commit format in PR titles to trigger releases via release-please
task in drone pipeline.
chore:
&build:
commits don't trigger releases (used for changes like updating config files or documentation)fix:
bumps the patch versionfeat:
bumps the minor versionfeat!:
bumps the major version
Formatting / Linting
$ cargo fmt --manifest-path decider-py/test/Cargo.toml
$ cargo clippy --manifest-path decider-py/test/Cargo.toml
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
reddit_decider-1.5.6.tar.gz
(60.7 kB
view hashes)
Built Distributions
Close
Hashes for reddit_decider-1.5.6-cp37-abi3-musllinux_1_1_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e2299f707db6f71c88fa545e647460efb83deab8c7c619d0b66466e7b9a07df2 |
|
MD5 | 871d0c406b93c20d63ccc11e85f6b2c3 |
|
BLAKE2b-256 | ab6d5bf41a3865f450ec7797764589da0d08a84309bb4e50f874df3445b98193 |
Close
Hashes for reddit_decider-1.5.6-cp37-abi3-manylinux_2_28_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8197c9d6cafa6f434dbb0e938ac27673ddfac36e310ba8365e5122f95c8503b9 |
|
MD5 | 203fc439edc456e2b7c61927ebc19939 |
|
BLAKE2b-256 | a6fb42d400bdb230de5fab2b0ecbf44b3fe959991ccebba677f7688a05249f7e |
Close
Hashes for reddit_decider-1.5.6-cp37-abi3-macosx_11_0_arm64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 02bc1fa425688b4bfc9c32bdda98eaeb7e2aeb6f3c4f72d32570ce863929b551 |
|
MD5 | aebc4a0baeb2c9db8cacadf4b145e9ee |
|
BLAKE2b-256 | 0537736b4b2642f372fb5bdd2d06041c7005b7ed6cb3aef6ce644b2d9ded2cec |
Close
Hashes for reddit_decider-1.5.6-cp37-abi3-macosx_10_7_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a8f4f48e574c46a85262f0d8b1e6611c7d5a6c5dac6488c962e762528286bb28 |
|
MD5 | bbd712e3cf173f370bc8ae6468809260 |
|
BLAKE2b-256 | c533afaf42a7ecf4e228e1ae3061b29a42e1fcc84f69692b30ccf79cef3ca0ab |