openfeature-provider-flagd 0.1.5__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. openfeature/.gitignore +2 -0
  2. openfeature/contrib/provider/flagd/config.py +214 -23
  3. openfeature/contrib/provider/flagd/provider.py +88 -12
  4. openfeature/contrib/provider/flagd/resolvers/__init__.py +1 -47
  5. openfeature/contrib/provider/flagd/resolvers/grpc.py +229 -17
  6. openfeature/contrib/provider/flagd/resolvers/in_process.py +40 -31
  7. openfeature/contrib/provider/flagd/resolvers/process/connector/__init__.py +11 -0
  8. openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py +107 -0
  9. openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py +218 -0
  10. openfeature/contrib/provider/flagd/resolvers/process/custom_ops.py +58 -19
  11. openfeature/contrib/provider/flagd/resolvers/process/flags.py +50 -6
  12. openfeature/contrib/provider/flagd/resolvers/process/targeting.py +35 -0
  13. openfeature/contrib/provider/flagd/resolvers/protocol.py +47 -0
  14. openfeature/contrib/provider/flagd/sync_metadata_hook.py +14 -0
  15. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.py +72 -0
  16. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2.pyi +450 -0
  17. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.py +358 -0
  18. openfeature/schemas/protobuf/flagd/evaluation/v1/evaluation_pb2_grpc.pyi +155 -0
  19. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.py +50 -0
  20. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2.pyi +148 -0
  21. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.py +186 -0
  22. openfeature/schemas/protobuf/flagd/sync/v1/sync_pb2_grpc.pyi +86 -0
  23. openfeature/schemas/protobuf/schema/v1/schema_pb2.py +72 -0
  24. openfeature/schemas/protobuf/schema/v1/schema_pb2.pyi +451 -0
  25. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.py +358 -0
  26. openfeature/schemas/protobuf/schema/v1/schema_pb2_grpc.pyi +156 -0
  27. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.py +47 -0
  28. openfeature/schemas/protobuf/sync/v1/sync_service_pb2.pyi +174 -0
  29. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.py +143 -0
  30. openfeature/schemas/protobuf/sync/v1/sync_service_pb2_grpc.pyi +70 -0
  31. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/METADATA +116 -15
  32. openfeature_provider_flagd-0.2.1.dist-info/RECORD +36 -0
  33. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/WHEEL +1 -1
  34. {openfeature_provider_flagd-0.1.5.dist-info → openfeature_provider_flagd-0.2.1.dist-info}/licenses/LICENSE +1 -1
  35. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2.py +0 -62
  36. openfeature/contrib/provider/flagd/proto/flagd/evaluation/v1/evaluation_pb2_grpc.py +0 -267
  37. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2.py +0 -40
  38. openfeature/contrib/provider/flagd/proto/flagd/sync/v1/sync_pb2_grpc.py +0 -135
  39. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2.py +0 -62
  40. openfeature/contrib/provider/flagd/proto/schema/v1/schema_pb2_grpc.py +0 -267
  41. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2.py +0 -37
  42. openfeature/contrib/provider/flagd/proto/sync/v1/sync_service_pb2_grpc.py +0 -102
  43. openfeature/contrib/provider/flagd/resolvers/process/file_watcher.py +0 -89
  44. openfeature_provider_flagd-0.1.5.dist-info/RECORD +0 -22
openfeature/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+
2
+ schemas
@@ -1,7 +1,58 @@
1
+ import dataclasses
1
2
  import os
2
3
  import typing
3
4
  from enum import Enum
4
5
 
6
+ import grpc
7
+
8
+
9
+ class ResolverType(Enum):
10
+ RPC = "rpc"
11
+ IN_PROCESS = "in-process"
12
+ FILE = "file"
13
+
14
+
15
+ class CacheType(Enum):
16
+ LRU = "lru"
17
+ DISABLED = "disabled"
18
+
19
+
20
+ DEFAULT_CACHE = CacheType.LRU
21
+ DEFAULT_CACHE_SIZE = 1000
22
+ DEFAULT_DEADLINE = 500
23
+ DEFAULT_HOST = "localhost"
24
+ DEFAULT_KEEP_ALIVE = 0
25
+ DEFAULT_OFFLINE_SOURCE_PATH: typing.Optional[str] = None
26
+ DEFAULT_OFFLINE_POLL_MS = 5000
27
+ DEFAULT_PORT_IN_PROCESS = 8015
28
+ DEFAULT_PORT_RPC = 8013
29
+ DEFAULT_RESOLVER_TYPE = ResolverType.RPC
30
+ DEFAULT_RETRY_BACKOFF = 1000
31
+ DEFAULT_RETRY_BACKOFF_MAX = 120000
32
+ DEFAULT_RETRY_GRACE_PERIOD_SECONDS = 5
33
+ DEFAULT_STREAM_DEADLINE = 600000
34
+ DEFAULT_TLS = False
35
+ DEFAULT_TLS_CERT: typing.Optional[str] = None
36
+
37
+ ENV_VAR_CACHE_SIZE = "FLAGD_MAX_CACHE_SIZE"
38
+ ENV_VAR_CACHE_TYPE = "FLAGD_CACHE"
39
+ ENV_VAR_DEADLINE_MS = "FLAGD_DEADLINE_MS"
40
+ ENV_VAR_HOST = "FLAGD_HOST"
41
+ ENV_VAR_KEEP_ALIVE_TIME_MS = "FLAGD_KEEP_ALIVE_TIME_MS"
42
+ ENV_VAR_OFFLINE_FLAG_SOURCE_PATH = "FLAGD_OFFLINE_FLAG_SOURCE_PATH"
43
+ ENV_VAR_OFFLINE_POLL_MS = "FLAGD_OFFLINE_POLL_MS"
44
+ ENV_VAR_PORT = "FLAGD_PORT"
45
+ ENV_VAR_RESOLVER_TYPE = "FLAGD_RESOLVER"
46
+ ENV_VAR_RETRY_BACKOFF_MS = "FLAGD_RETRY_BACKOFF_MS"
47
+ ENV_VAR_RETRY_BACKOFF_MAX_MS = "FLAGD_RETRY_BACKOFF_MAX_MS"
48
+ ENV_VAR_RETRY_GRACE_PERIOD_SECONDS = "FLAGD_RETRY_GRACE_PERIOD"
49
+ ENV_VAR_SELECTOR = "FLAGD_SOURCE_SELECTOR"
50
+ ENV_VAR_PROVIDER_ID = "FLAGD_SOURCE_PROVIDER_ID"
51
+ ENV_VAR_STREAM_DEADLINE_MS = "FLAGD_STREAM_DEADLINE_MS"
52
+ ENV_VAR_TLS = "FLAGD_TLS"
53
+ ENV_VAR_TLS_CERT = "FLAGD_SERVER_CERT_PATH"
54
+ ENV_VAR_DEFAULT_AUTHORITY = "FLAGD_DEFAULT_AUTHORITY"
55
+
5
56
  T = typing.TypeVar("T")
6
57
 
7
58
 
@@ -9,6 +60,14 @@ def str_to_bool(val: str) -> bool:
9
60
  return val.lower() == "true"
10
61
 
11
62
 
63
+ def convert_resolver_type(val: typing.Union[str, ResolverType]) -> ResolverType:
64
+ if isinstance(val, str):
65
+ v = val.lower()
66
+ return ResolverType(v)
67
+ else:
68
+ return ResolverType(val)
69
+
70
+
12
71
  def env_or_default(
13
72
  env_var: str, default: T, cast: typing.Optional[typing.Callable[[str], T]] = None
14
73
  ) -> typing.Union[str, T]:
@@ -18,42 +77,174 @@ def env_or_default(
18
77
  return val if cast is None else cast(val)
19
78
 
20
79
 
21
- class ResolverType(Enum):
22
- GRPC = "grpc"
23
- IN_PROCESS = "in-process"
24
-
25
-
80
+ @dataclasses.dataclass
26
81
  class Config:
27
82
  def __init__( # noqa: PLR0913
28
83
  self,
29
84
  host: typing.Optional[str] = None,
30
85
  port: typing.Optional[int] = None,
31
86
  tls: typing.Optional[bool] = None,
32
- timeout: typing.Optional[int] = None,
33
- resolver_type: typing.Optional[ResolverType] = None,
87
+ selector: typing.Optional[str] = None,
88
+ provider_id: typing.Optional[str] = None,
89
+ resolver: typing.Optional[ResolverType] = None,
34
90
  offline_flag_source_path: typing.Optional[str] = None,
35
- offline_poll_interval_seconds: typing.Optional[float] = None,
91
+ offline_poll_interval_ms: typing.Optional[int] = None,
92
+ retry_backoff_ms: typing.Optional[int] = None,
93
+ retry_backoff_max_ms: typing.Optional[int] = None,
94
+ retry_grace_period: typing.Optional[int] = None,
95
+ deadline_ms: typing.Optional[int] = None,
96
+ stream_deadline_ms: typing.Optional[int] = None,
97
+ keep_alive_time: typing.Optional[int] = None,
98
+ cache: typing.Optional[CacheType] = None,
99
+ max_cache_size: typing.Optional[int] = None,
100
+ cert_path: typing.Optional[str] = None,
101
+ default_authority: typing.Optional[str] = None,
102
+ channel_credentials: typing.Optional[grpc.ChannelCredentials] = None,
36
103
  ):
37
- self.host = env_or_default("FLAGD_HOST", "localhost") if host is None else host
38
- self.port = (
39
- env_or_default("FLAGD_PORT", 8013, cast=int) if port is None else port
40
- )
104
+ self.host = env_or_default(ENV_VAR_HOST, DEFAULT_HOST) if host is None else host
105
+
41
106
  self.tls = (
42
- env_or_default("FLAGD_TLS", False, cast=str_to_bool) if tls is None else tls
107
+ env_or_default(ENV_VAR_TLS, DEFAULT_TLS, cast=str_to_bool)
108
+ if tls is None
109
+ else tls
110
+ )
111
+
112
+ self.retry_backoff_ms: int = (
113
+ int(
114
+ env_or_default(
115
+ ENV_VAR_RETRY_BACKOFF_MS, DEFAULT_RETRY_BACKOFF, cast=int
116
+ )
117
+ )
118
+ if retry_backoff_ms is None
119
+ else retry_backoff_ms
43
120
  )
44
- self.timeout = 5 if timeout is None else timeout
45
- self.resolver_type = (
46
- ResolverType(env_or_default("FLAGD_RESOLVER_TYPE", "grpc"))
47
- if resolver_type is None
48
- else resolver_type
121
+ self.retry_backoff_max_ms: int = (
122
+ int(
123
+ env_or_default(
124
+ ENV_VAR_RETRY_BACKOFF_MAX_MS, DEFAULT_RETRY_BACKOFF_MAX, cast=int
125
+ )
126
+ )
127
+ if retry_backoff_max_ms is None
128
+ else retry_backoff_max_ms
129
+ )
130
+
131
+ self.retry_grace_period: int = (
132
+ int(
133
+ env_or_default(
134
+ ENV_VAR_RETRY_GRACE_PERIOD_SECONDS,
135
+ DEFAULT_RETRY_GRACE_PERIOD_SECONDS,
136
+ cast=int,
137
+ )
138
+ )
139
+ if retry_grace_period is None
140
+ else retry_grace_period
141
+ )
142
+
143
+ self.resolver = (
144
+ env_or_default(
145
+ ENV_VAR_RESOLVER_TYPE, DEFAULT_RESOLVER_TYPE, cast=convert_resolver_type
146
+ )
147
+ if resolver is None
148
+ else resolver
149
+ )
150
+
151
+ default_port = (
152
+ DEFAULT_PORT_RPC
153
+ if self.resolver is ResolverType.RPC
154
+ else DEFAULT_PORT_IN_PROCESS
49
155
  )
156
+
157
+ self.port: int = (
158
+ int(env_or_default(ENV_VAR_PORT, default_port, cast=int))
159
+ if port is None
160
+ else port
161
+ )
162
+
50
163
  self.offline_flag_source_path = (
51
- env_or_default("FLAGD_OFFLINE_FLAG_SOURCE_PATH", None)
164
+ env_or_default(
165
+ ENV_VAR_OFFLINE_FLAG_SOURCE_PATH, DEFAULT_OFFLINE_SOURCE_PATH
166
+ )
52
167
  if offline_flag_source_path is None
53
168
  else offline_flag_source_path
54
169
  )
55
- self.offline_poll_interval_seconds = (
56
- float(env_or_default("FLAGD_OFFLINE_POLL_INTERVAL_SECONDS", 1.0))
57
- if offline_poll_interval_seconds is None
58
- else offline_poll_interval_seconds
170
+
171
+ if (
172
+ self.offline_flag_source_path is not None
173
+ and self.resolver is ResolverType.IN_PROCESS
174
+ ):
175
+ self.resolver = ResolverType.FILE
176
+
177
+ if self.resolver is ResolverType.FILE and self.offline_flag_source_path is None:
178
+ raise AttributeError(
179
+ "Resolver Type 'FILE' requires a offlineFlagSourcePath"
180
+ )
181
+
182
+ self.offline_poll_interval_ms: int = (
183
+ int(
184
+ env_or_default(
185
+ ENV_VAR_OFFLINE_POLL_MS, DEFAULT_OFFLINE_POLL_MS, cast=int
186
+ )
187
+ )
188
+ if offline_poll_interval_ms is None
189
+ else offline_poll_interval_ms
59
190
  )
191
+
192
+ self.deadline_ms: int = (
193
+ int(env_or_default(ENV_VAR_DEADLINE_MS, DEFAULT_DEADLINE, cast=int))
194
+ if deadline_ms is None
195
+ else deadline_ms
196
+ )
197
+
198
+ self.stream_deadline_ms: int = (
199
+ int(
200
+ env_or_default(
201
+ ENV_VAR_STREAM_DEADLINE_MS, DEFAULT_STREAM_DEADLINE, cast=int
202
+ )
203
+ )
204
+ if stream_deadline_ms is None
205
+ else stream_deadline_ms
206
+ )
207
+
208
+ self.keep_alive_time: int = (
209
+ int(
210
+ env_or_default(ENV_VAR_KEEP_ALIVE_TIME_MS, DEFAULT_KEEP_ALIVE, cast=int)
211
+ )
212
+ if keep_alive_time is None
213
+ else keep_alive_time
214
+ )
215
+
216
+ self.cache = (
217
+ CacheType(env_or_default(ENV_VAR_CACHE_TYPE, DEFAULT_CACHE))
218
+ if cache is None
219
+ else cache
220
+ )
221
+
222
+ self.max_cache_size: int = (
223
+ int(env_or_default(ENV_VAR_CACHE_SIZE, DEFAULT_CACHE_SIZE, cast=int))
224
+ if max_cache_size is None
225
+ else max_cache_size
226
+ )
227
+
228
+ self.cert_path = (
229
+ env_or_default(ENV_VAR_TLS_CERT, DEFAULT_TLS_CERT)
230
+ if cert_path is None
231
+ else cert_path
232
+ )
233
+
234
+ self.selector = (
235
+ env_or_default(ENV_VAR_SELECTOR, None) if selector is None else selector
236
+ )
237
+
238
+ self.provider_id = (
239
+ env_or_default(ENV_VAR_PROVIDER_ID, None)
240
+ if provider_id is None
241
+ else provider_id
242
+ )
243
+
244
+ self.default_authority = (
245
+ env_or_default(ENV_VAR_DEFAULT_AUTHORITY, None)
246
+ if default_authority is None
247
+ else default_authority
248
+ )
249
+
250
+ self.channel_credentials = channel_credentials
@@ -22,14 +22,20 @@
22
22
  """
23
23
 
24
24
  import typing
25
+ import warnings
26
+
27
+ import grpc
25
28
 
26
29
  from openfeature.evaluation_context import EvaluationContext
30
+ from openfeature.event import ProviderEventDetails
27
31
  from openfeature.flag_evaluation import FlagResolutionDetails
32
+ from openfeature.hook import Hook
33
+ from openfeature.provider import AbstractProvider
28
34
  from openfeature.provider.metadata import Metadata
29
- from openfeature.provider.provider import AbstractProvider
30
35
 
31
- from .config import Config, ResolverType
36
+ from .config import CacheType, Config, ResolverType
32
37
  from .resolvers import AbstractResolver, GrpcResolver, InProcessResolver
38
+ from .sync_metadata_hook import SyncMetadataHook
33
39
 
34
40
  T = typing.TypeVar("T")
35
41
 
@@ -42,10 +48,22 @@ class FlagdProvider(AbstractProvider):
42
48
  host: typing.Optional[str] = None,
43
49
  port: typing.Optional[int] = None,
44
50
  tls: typing.Optional[bool] = None,
51
+ deadline_ms: typing.Optional[int] = None,
45
52
  timeout: typing.Optional[int] = None,
53
+ retry_backoff_ms: typing.Optional[int] = None,
54
+ selector: typing.Optional[str] = None,
55
+ provider_id: typing.Optional[str] = None,
46
56
  resolver_type: typing.Optional[ResolverType] = None,
47
57
  offline_flag_source_path: typing.Optional[str] = None,
48
- offline_poll_interval_seconds: typing.Optional[float] = None,
58
+ stream_deadline_ms: typing.Optional[int] = None,
59
+ keep_alive_time: typing.Optional[int] = None,
60
+ cache: typing.Optional[CacheType] = None,
61
+ max_cache_size: typing.Optional[int] = None,
62
+ retry_backoff_max_ms: typing.Optional[int] = None,
63
+ retry_grace_period: typing.Optional[int] = None,
64
+ cert_path: typing.Optional[str] = None,
65
+ default_authority: typing.Optional[str] = None,
66
+ channel_credentials: typing.Optional[grpc.ChannelCredentials] = None,
49
67
  ):
50
68
  """
51
69
  Create an instance of the FlagdProvider
@@ -53,30 +71,81 @@ class FlagdProvider(AbstractProvider):
53
71
  :param host: the host to make requests to
54
72
  :param port: the port the flagd service is available on
55
73
  :param tls: enable/disable secure TLS connectivity
56
- :param timeout: the maximum to wait before a request times out
74
+ :param deadline_ms: the maximum to wait before a request times out
75
+ :param timeout: the maximum time to wait before a request times out
76
+ :param retry_backoff_ms: the number of milliseconds to backoff
77
+ :param offline_flag_source_path: the path to the flag source file
78
+ :param stream_deadline_ms: the maximum time to wait before a request times out
79
+ :param keep_alive_time: the number of milliseconds to keep alive
80
+ :param resolver_type: the type of resolver to use
57
81
  """
82
+ if deadline_ms is None and timeout is not None:
83
+ deadline_ms = timeout * 1000
84
+ warnings.warn(
85
+ "'timeout' property is deprecated, please use 'deadline' instead, be aware that 'deadline' is in milliseconds",
86
+ DeprecationWarning,
87
+ stacklevel=2,
88
+ )
89
+
58
90
  self.config = Config(
59
91
  host=host,
60
92
  port=port,
61
93
  tls=tls,
62
- timeout=timeout,
63
- resolver_type=resolver_type,
94
+ deadline_ms=deadline_ms,
95
+ retry_backoff_ms=retry_backoff_ms,
96
+ retry_backoff_max_ms=retry_backoff_max_ms,
97
+ retry_grace_period=retry_grace_period,
98
+ selector=selector,
99
+ provider_id=provider_id,
100
+ resolver=resolver_type,
64
101
  offline_flag_source_path=offline_flag_source_path,
65
- offline_poll_interval_seconds=offline_poll_interval_seconds,
102
+ stream_deadline_ms=stream_deadline_ms,
103
+ keep_alive_time=keep_alive_time,
104
+ cache=cache,
105
+ max_cache_size=max_cache_size,
106
+ cert_path=cert_path,
107
+ default_authority=default_authority,
108
+ channel_credentials=channel_credentials,
66
109
  )
110
+ self.enriched_context: dict = {}
67
111
 
68
112
  self.resolver = self.setup_resolver()
113
+ self.hooks: list[Hook] = [SyncMetadataHook(self.get_enriched_context)]
114
+
115
+ def get_enriched_context(self) -> EvaluationContext:
116
+ return EvaluationContext(attributes=self.enriched_context)
117
+
118
+ def get_provider_hooks(self) -> list[Hook]:
119
+ return self.hooks
69
120
 
70
121
  def setup_resolver(self) -> AbstractResolver:
71
- if self.config.resolver_type == ResolverType.GRPC:
72
- return GrpcResolver(self.config)
73
- elif self.config.resolver_type == ResolverType.IN_PROCESS:
74
- return InProcessResolver(self.config, self)
122
+ if self.config.resolver == ResolverType.RPC:
123
+ return GrpcResolver(
124
+ self.config,
125
+ self.emit_provider_ready,
126
+ self.emit_provider_error,
127
+ self.emit_provider_stale,
128
+ self.emit_provider_configuration_changed,
129
+ )
130
+ elif (
131
+ self.config.resolver == ResolverType.IN_PROCESS
132
+ or self.config.resolver == ResolverType.FILE
133
+ ):
134
+ return InProcessResolver(
135
+ self.config,
136
+ self.emit_provider_ready_with_context,
137
+ self.emit_provider_error,
138
+ self.emit_provider_stale,
139
+ self.emit_provider_configuration_changed,
140
+ )
75
141
  else:
76
142
  raise ValueError(
77
- f"`resolver_type` parameter invalid: {self.config.resolver_type}"
143
+ f"`resolver_type` parameter invalid: {self.config.resolver}"
78
144
  )
79
145
 
146
+ def initialize(self, evaluation_context: EvaluationContext) -> None:
147
+ self.resolver.initialize(evaluation_context)
148
+
80
149
  def shutdown(self) -> None:
81
150
  if self.resolver:
82
151
  self.resolver.shutdown()
@@ -134,3 +203,10 @@ class FlagdProvider(AbstractProvider):
134
203
  return self.resolver.resolve_object_details(
135
204
  key, default_value, evaluation_context
136
205
  )
206
+
207
+ def emit_provider_ready_with_context(
208
+ self, details: ProviderEventDetails, context: dict
209
+ ) -> None:
210
+ self.enriched_context = context
211
+ self.emit_provider_ready(details)
212
+ pass
@@ -1,51 +1,5 @@
1
- import typing
2
-
3
- from typing_extensions import Protocol
4
-
5
- from openfeature.evaluation_context import EvaluationContext
6
- from openfeature.flag_evaluation import FlagResolutionDetails
7
-
8
1
  from .grpc import GrpcResolver
9
2
  from .in_process import InProcessResolver
10
-
11
-
12
- class AbstractResolver(Protocol):
13
- def shutdown(self) -> None: ...
14
-
15
- def resolve_boolean_details(
16
- self,
17
- key: str,
18
- default_value: bool,
19
- evaluation_context: typing.Optional[EvaluationContext] = None,
20
- ) -> FlagResolutionDetails[bool]: ...
21
-
22
- def resolve_string_details(
23
- self,
24
- key: str,
25
- default_value: str,
26
- evaluation_context: typing.Optional[EvaluationContext] = None,
27
- ) -> FlagResolutionDetails[str]: ...
28
-
29
- def resolve_float_details(
30
- self,
31
- key: str,
32
- default_value: float,
33
- evaluation_context: typing.Optional[EvaluationContext] = None,
34
- ) -> FlagResolutionDetails[float]: ...
35
-
36
- def resolve_integer_details(
37
- self,
38
- key: str,
39
- default_value: int,
40
- evaluation_context: typing.Optional[EvaluationContext] = None,
41
- ) -> FlagResolutionDetails[int]: ...
42
-
43
- def resolve_object_details(
44
- self,
45
- key: str,
46
- default_value: typing.Union[dict, list],
47
- evaluation_context: typing.Optional[EvaluationContext] = None,
48
- ) -> FlagResolutionDetails[typing.Union[dict, list]]: ...
49
-
3
+ from .protocol import AbstractResolver
50
4
 
51
5
  __all__ = ["AbstractResolver", "GrpcResolver", "InProcessResolver"]