diff --git a/featureflags_client/http/conditions.py b/featureflags_client/http/conditions.py index 2adeca9..a49574e 100644 --- a/featureflags_client/http/conditions.py +++ b/featureflags_client/http/conditions.py @@ -3,6 +3,7 @@ from typing import Any, Callable, Dict, List, Optional, Set from featureflags_client.http.types import Check, Flag, Operator +from featureflags_client.http.utils import hash_flag_value log = logging.getLogger(__name__) @@ -79,7 +80,9 @@ def percent(name: str, value: Any) -> Callable: @except_false def proc(ctx: Dict[str, Any]) -> bool: ctx_val = ctx.get(name, _UNDEFINED) - return ctx_val is not _UNDEFINED and hash(ctx_val) % 100 < int(value) + hash_ctx_val = hash_flag_value(name, ctx_val) + + return ctx_val is not _UNDEFINED and hash_ctx_val % 100 < int(value) return proc diff --git a/featureflags_client/http/utils.py b/featureflags_client/http/utils.py index 05f001a..059f4fa 100644 --- a/featureflags_client/http/utils.py +++ b/featureflags_client/http/utils.py @@ -1,4 +1,6 @@ +import hashlib import inspect +import struct from enum import Enum, EnumMeta from typing import Any, Dict, Generator, Mapping, Type, Union @@ -54,3 +56,9 @@ def intervals_gen( else: success = yield retry_interval retry_interval = min(retry_interval * 2, retry_interval_max) + + +def hash_flag_value(name: str, value: Any) -> int: + hash_digest = hashlib.md5(f"{name}{value}".encode()).digest() # noqa: S324 + (hash_int,) = struct.unpack("