Skip to content

Commit fb56fe5

Browse files
authored
Merge pull request #88 from reddit/incorporate_dc_from_shim
Add DCs from decider-py shim (metrics)
2 parents 299ae13 + 3f94e66 commit fb56fe5

File tree

5 files changed

+63
-72
lines changed

5 files changed

+63
-72
lines changed

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ docutils==0.16
44
Jinja2==2.11.2
55
MarkupSafe==1.1.1
66
pydocstyle==5.1.1
7-
reddit-decider==1.2.29
7+
reddit-decider==1.2.30
88
reddit-edgecontext==1.0.0a3
99
Sphinx==3.4.0
1010
sphinx-autodoc-typehints==1.11.1

reddit_decider/__init__.py

Lines changed: 59 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from typing import IO
1111
from typing import List
1212
from typing import Optional
13+
from typing import Type
14+
from typing import TypeVar
1315
from typing import Union
1416

1517
from baseplate import RequestContext
@@ -27,13 +29,15 @@
2729
from rust_decider import Decision
2830
from rust_decider import FeatureNotFoundException
2931
from rust_decider import make_ctx
32+
from rust_decider import ValueTypeMismatchException
3033
from typing_extensions import Literal
3134

3235

3336
logger = logging.getLogger(__name__)
3437

3538
EMPLOYEE_ROLES = ["employee", "contractor"]
3639
IDENTIFIERS = ["user_id", "device_id", "canonical_url"]
40+
TYPE_STR_LOOKUP = {bool: "boolean", int: "integer", float: "float", str: "string", dict: "map"}
3741

3842

3943
class EventType(Enum):
@@ -58,6 +62,8 @@ class DeciderContext:
5862
:code:`DeciderContext()` is populated in :code:`make_object_for_context()`.
5963
"""
6064

65+
T = TypeVar("T")
66+
6167
def __init__(
6268
self,
6369
user_id: Optional[str] = None,
@@ -173,7 +179,7 @@ def __init__(
173179
event_logger: Optional[EventLogger] = None,
174180
):
175181
self._decider_context = decider_context
176-
self._internal = internal
182+
self._internal: RustDecider = internal
177183
self._span = server_span
178184
self._context_name = context_name
179185
if event_logger:
@@ -696,28 +702,6 @@ def get_all_variants_for_identifier_without_expose(
696702

697703
return parsed_choices
698704

699-
def _get_dynamic_config_value(
700-
self,
701-
feature_name: str,
702-
decider_func: Callable[[str, DeciderContext], Any],
703-
default: Any,
704-
) -> Optional[Any]:
705-
ctx = self._get_ctx()
706-
ctx_err = ctx.err()
707-
if ctx_err is not None:
708-
logger.info(f"Encountered error in rust_decider.make_ctx(): {ctx_err}")
709-
return None
710-
711-
res = decider_func(feature_name, ctx)
712-
if res is None:
713-
return default
714-
error = res.err()
715-
if error:
716-
logger.warning(f"Encountered error {decider_func.__name__}: {error}")
717-
return default
718-
719-
return res.val()
720-
721705
def get_bool(self, feature_name: str, default: bool = False) -> bool:
722706
"""Fetch a Dynamic Configuration of boolean type.
723707
@@ -728,10 +712,7 @@ def get_bool(self, feature_name: str, default: bool = False) -> bool:
728712
729713
:return: the boolean value of the dyanimc config if it is active/exists, :code:`default` parameter otherwise.
730714
"""
731-
decider = self._get_decider()
732-
if not decider:
733-
return default
734-
return self._get_dynamic_config_value(feature_name, decider.get_bool, default)
715+
return self._get_dynamic_config_value(feature_name, default, bool, self._internal.get_bool)
735716

736717
def get_int(self, feature_name: str, default: int = 0) -> int:
737718
"""Fetch a Dynamic Configuration of int type.
@@ -743,10 +724,7 @@ def get_int(self, feature_name: str, default: int = 0) -> int:
743724
744725
:return: the int value of the dyanimc config if it is active/exists, :code:`default` parameter otherwise.
745726
"""
746-
decider = self._get_decider()
747-
if not decider:
748-
return default
749-
return self._get_dynamic_config_value(feature_name, decider.get_int, default)
727+
return self._get_dynamic_config_value(feature_name, default, int, self._internal.get_int)
750728

751729
def get_float(self, feature_name: str, default: float = 0.0) -> float:
752730
"""Fetch a Dynamic Configuration of float type.
@@ -758,10 +736,9 @@ def get_float(self, feature_name: str, default: float = 0.0) -> float:
758736
759737
:return: the float value of the dyanimc config if it is active/exists, :code:`default` parameter otherwise.
760738
"""
761-
decider = self._get_decider()
762-
if not decider:
763-
return default
764-
return self._get_dynamic_config_value(feature_name, decider.get_float, default)
739+
return self._get_dynamic_config_value(
740+
feature_name, default, float, self._internal.get_float
741+
)
765742

766743
def get_string(self, feature_name: str, default: str = "") -> str:
767744
"""Fetch a Dynamic Configuration of string type.
@@ -773,10 +750,7 @@ def get_string(self, feature_name: str, default: str = "") -> str:
773750
774751
:return: the string value of the dyanimc config if it is active/exists, :code:`default` parameter otherwise.
775752
"""
776-
decider = self._get_decider()
777-
if not decider:
778-
return default
779-
return self._get_dynamic_config_value(feature_name, decider.get_string, default)
753+
return self._get_dynamic_config_value(feature_name, default, str, self._internal.get_string)
780754

781755
def get_map(self, feature_name: str, default: Optional[dict] = None) -> Optional[dict]:
782756
"""Fetch a Dynamic Configuration of map type.
@@ -788,10 +762,7 @@ def get_map(self, feature_name: str, default: Optional[dict] = None) -> Optional
788762
789763
:return: the map value of the dyanimc config if it is active/exists, :code:`default` parameter otherwise.
790764
"""
791-
decider = self._get_decider()
792-
if not decider:
793-
return default
794-
return self._get_dynamic_config_value(feature_name, decider.get_map, default)
765+
return self._get_dynamic_config_value(feature_name, default, dict, self._internal.get_map)
795766

796767
def get_all_dynamic_configs(self) -> List[Dict[str, Any]]:
797768
"""Return a list of dynamic configuration dicts in this format:
@@ -826,40 +797,61 @@ def get_all_dynamic_configs(self) -> List[Dict[str, Any]]:
826797
827798
:return: list of all active dynamic config dicts.
828799
"""
829-
decider = self._get_decider()
830-
if not decider:
831-
return []
832-
833-
ctx = self._get_ctx()
834-
ctx_err = ctx.err()
835-
if ctx_err is not None:
836-
logger.info(f"Encountered error in rust_decider.make_ctx(): {ctx_err}")
800+
if self._internal is None:
801+
logger.error("rs_decider is None--did not initialize.")
837802
return []
838803

839-
all_decisions_result = decider.get_all_values(ctx)
804+
ctx = self._decider_context.to_dict()
840805

841-
error = all_decisions_result.err()
842-
if error:
843-
logger.info(f"Encountered error in decider.choose_all(): {error}")
806+
try:
807+
values = self._internal.all_values(ctx)
808+
except DeciderException as exc:
809+
logger.info(str(exc))
844810
return []
845811

846-
all_decisions = all_decisions_result.decisions()
847812
parsed_configs = []
848813

849-
for dc_name, decision in all_decisions.items():
850-
decision_error = decision.err()
851-
if decision_error:
852-
logger.info(
853-
f"Encountered error for dynamic config: {dc_name} in decider.get_all_values(): {decision_error}"
854-
)
855-
continue
814+
for feature_name, val in values.items():
815+
parsed_configs.append(self._value_to_dc_dict(feature_name, val))
816+
817+
return parsed_configs
856818

857-
value_dict = decision.value_dict()
819+
def _get_dynamic_config_value(
820+
self,
821+
feature_name: str,
822+
default: Any,
823+
dc_type: Type[T],
824+
get_fn: Callable[..., Type[T]],
825+
) -> T:
826+
if self._internal is None:
827+
logger.error("rs_decider is None--did not initialize.")
828+
return default
858829

859-
if value_dict:
860-
parsed_configs.append(value_dict)
830+
ctx = self._decider_context.to_dict()
861831

862-
return parsed_configs
832+
try:
833+
value = get_fn(feature_name=feature_name, context=ctx)
834+
except FeatureNotFoundException as exc:
835+
warnings.warn(str(exc))
836+
return default
837+
except ValueTypeMismatchException as exc:
838+
logger.info(str(exc))
839+
return default
840+
except DeciderException as exc:
841+
logger.info(str(exc))
842+
return default
843+
844+
try:
845+
return dc_type(value) # type: ignore [call-arg]
846+
except TypeError:
847+
return default
848+
849+
def _value_to_dc_dict(self, feature_name: str, value: Optional[Any]) -> Dict[str, Any]:
850+
return {
851+
"name": feature_name,
852+
"value": value,
853+
"type": "" if value is None else TYPE_STR_LOOKUP[type(value)],
854+
}
863855

864856
def get_experiment(self, experiment_name: str) -> Optional[ExperimentConfig]:
865857
"""Get an :py:class:`~reddit_decider.ExperimentConfig` `dataclass <https://github.com/reddit/experiments.py/blob/develop/reddit_decider/__init__.py#L44>`_

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-r requirements-transitive.txt
22
baseplate==2.0.0a1
33
black==21.4b2
4-
reddit-decider==1.2.29
4+
reddit-decider==1.2.30
55
flake8==3.9.1
66
mypy==0.790
77
pyramid==2.0 # required for `from baseplate.frameworks.pyramid import BaseplateRequest` which calls `import pyramid.events`

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
install_requires=[
2020
"baseplate>=2.0.0a1,<3.0",
2121
"reddit-edgecontext>=1.0.0a3,<2.0",
22-
"reddit-decider~=1.2.29",
22+
"reddit-decider~=1.2.30",
2323
"typing_extensions>=3.10.0.0,<5.0",
2424
],
2525
package_data={"reddit_experiments": ["py.typed"]},

tests/decider_tests.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,11 +1696,10 @@ def test_get_all_values(self):
16961696
{"name": "dc_missing_map", "value": {}, "type": "map"},
16971697
)
16981698

1699-
# set "type" to empty string if "value_type" is missing on cfg
17001699
missing_map_val_res = first_occurrence_of_key_in(
17011700
configs, "name", "dc_missing_value_type"
17021701
)
17031702
self.assertEqual(
17041703
missing_map_val_res,
1705-
{"name": "dc_missing_value_type", "value": False, "type": ""},
1704+
{"name": "dc_missing_value_type", "value": False, "type": "boolean"},
17061705
)

0 commit comments

Comments
 (0)