Skip to content

Commit af9d3da

Browse files
chore: enable mypy strict mode (#257)
Signed-off-by: gruebel <[email protected]> Co-authored-by: Federico Bond <[email protected]>
1 parent 30f4e69 commit af9d3da

File tree

8 files changed

+70
-39
lines changed

8 files changed

+70
-39
lines changed

openfeature/_backports/strenum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import sys
22

33
if sys.version_info >= (3, 11):
4-
from enum import StrEnum
4+
# re-export needed for type checking
5+
from enum import StrEnum as StrEnum # noqa: PLC0414
56
else:
67
from enum import Enum
78

openfeature/api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_client(
2121
return OpenFeatureClient(name=name, version=version, provider=_provider)
2222

2323

24-
def set_provider(provider: AbstractProvider):
24+
def set_provider(provider: AbstractProvider) -> None:
2525
global _provider
2626
if provider is None:
2727
raise GeneralError(error_message="No provider")
@@ -46,19 +46,19 @@ def get_evaluation_context() -> EvaluationContext:
4646
return _evaluation_context
4747

4848

49-
def set_evaluation_context(evaluation_context: EvaluationContext):
49+
def set_evaluation_context(evaluation_context: EvaluationContext) -> None:
5050
global _evaluation_context
5151
if evaluation_context is None:
5252
raise GeneralError(error_message="No api level evaluation context")
5353
_evaluation_context = evaluation_context
5454

5555

56-
def add_hooks(hooks: typing.List[Hook]):
56+
def add_hooks(hooks: typing.List[Hook]) -> None:
5757
global _hooks
5858
_hooks = _hooks + hooks
5959

6060

61-
def clear_hooks():
61+
def clear_hooks() -> None:
6262
global _hooks
6363
_hooks = []
6464

@@ -68,5 +68,5 @@ def get_hooks() -> typing.List[Hook]:
6868
return _hooks
6969

7070

71-
def shutdown():
71+
def shutdown() -> None:
7272
_provider.shutdown()

openfeature/client.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,21 @@
4545
FlagResolutionDetails[typing.Union[dict, list]],
4646
],
4747
]
48+
TypeMap = typing.Dict[
49+
FlagType,
50+
typing.Union[
51+
typing.Type[bool],
52+
typing.Type[int],
53+
typing.Type[float],
54+
typing.Type[str],
55+
typing.Tuple[typing.Type[dict], typing.Type[list]],
56+
],
57+
]
4858

4959

5060
@dataclass
5161
class ClientMetadata:
52-
name: str
62+
name: typing.Optional[str]
5363

5464

5565
class OpenFeatureClient:
@@ -60,17 +70,17 @@ def __init__(
6070
provider: AbstractProvider,
6171
context: typing.Optional[EvaluationContext] = None,
6272
hooks: typing.Optional[typing.List[Hook]] = None,
63-
):
73+
) -> None:
6474
self.name = name
6575
self.version = version
6676
self.context = context or EvaluationContext()
6777
self.hooks = hooks or []
6878
self.provider = provider
6979

70-
def get_metadata(self):
80+
def get_metadata(self) -> ClientMetadata:
7181
return ClientMetadata(name=self.name)
7282

73-
def add_hooks(self, hooks: typing.List[Hook]):
83+
def add_hooks(self, hooks: typing.List[Hook]) -> None:
7484
self.hooks = self.hooks + hooks
7585

7686
def get_boolean_value(
@@ -93,7 +103,7 @@ def get_boolean_details(
93103
default_value: bool,
94104
evaluation_context: typing.Optional[EvaluationContext] = None,
95105
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
96-
) -> FlagEvaluationDetails:
106+
) -> FlagEvaluationDetails[bool]:
97107
return self.evaluate_flag_details(
98108
FlagType.BOOLEAN,
99109
flag_key,
@@ -122,7 +132,7 @@ def get_string_details(
122132
default_value: str,
123133
evaluation_context: typing.Optional[EvaluationContext] = None,
124134
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
125-
) -> FlagEvaluationDetails:
135+
) -> FlagEvaluationDetails[str]:
126136
return self.evaluate_flag_details(
127137
FlagType.STRING,
128138
flag_key,
@@ -151,7 +161,7 @@ def get_integer_details(
151161
default_value: int,
152162
evaluation_context: typing.Optional[EvaluationContext] = None,
153163
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
154-
) -> FlagEvaluationDetails:
164+
) -> FlagEvaluationDetails[int]:
155165
return self.evaluate_flag_details(
156166
FlagType.INTEGER,
157167
flag_key,
@@ -180,7 +190,7 @@ def get_float_details(
180190
default_value: float,
181191
evaluation_context: typing.Optional[EvaluationContext] = None,
182192
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
183-
) -> FlagEvaluationDetails:
193+
) -> FlagEvaluationDetails[float]:
184194
return self.evaluate_flag_details(
185195
FlagType.FLOAT,
186196
flag_key,
@@ -195,7 +205,7 @@ def get_object_value(
195205
default_value: typing.Union[dict, list],
196206
evaluation_context: typing.Optional[EvaluationContext] = None,
197207
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
198-
) -> dict:
208+
) -> typing.Union[dict, list]:
199209
return self.get_object_details(
200210
flag_key,
201211
default_value,
@@ -209,7 +219,7 @@ def get_object_details(
209219
default_value: typing.Union[dict, list],
210220
evaluation_context: typing.Optional[EvaluationContext] = None,
211221
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
212-
) -> FlagEvaluationDetails:
222+
) -> FlagEvaluationDetails[typing.Union[dict, list]]:
213223
return self.evaluate_flag_details(
214224
FlagType.OBJECT,
215225
flag_key,
@@ -225,7 +235,7 @@ def evaluate_flag_details(
225235
default_value: typing.Any,
226236
evaluation_context: typing.Optional[EvaluationContext] = None,
227237
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
228-
) -> FlagEvaluationDetails:
238+
) -> FlagEvaluationDetails[typing.Any]:
229239
"""
230240
Evaluate the flag requested by the user from the clients provider.
231241
@@ -335,7 +345,7 @@ def _create_provider_evaluation(
335345
flag_key: str,
336346
default_value: typing.Any,
337347
evaluation_context: typing.Optional[EvaluationContext] = None,
338-
) -> FlagEvaluationDetails:
348+
) -> FlagEvaluationDetails[typing.Any]:
339349
"""
340350
Encapsulated method to create a FlagEvaluationDetail from a specific provider.
341351
@@ -384,8 +394,8 @@ def _create_provider_evaluation(
384394
)
385395

386396

387-
def _typecheck_flag_value(value, flag_type):
388-
type_map = {
397+
def _typecheck_flag_value(value: typing.Any, flag_type: FlagType) -> None:
398+
type_map: TypeMap = {
389399
FlagType.BOOLEAN: bool,
390400
FlagType.STRING: str,
391401
FlagType.OBJECT: (dict, list),

openfeature/hook/__init__.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ def before(
4646
return None
4747

4848
def after(
49-
self, hook_context: HookContext, details: FlagEvaluationDetails, hints: dict
50-
):
49+
self,
50+
hook_context: HookContext,
51+
details: FlagEvaluationDetails[typing.Any],
52+
hints: dict,
53+
) -> None:
5154
"""
5255
Runs after a flag is resolved.
5356
@@ -58,7 +61,9 @@ def after(
5861
"""
5962
pass
6063

61-
def error(self, hook_context: HookContext, exception: Exception, hints: dict):
64+
def error(
65+
self, hook_context: HookContext, exception: Exception, hints: dict
66+
) -> None:
6267
"""
6368
Run when evaluation encounters an error. Errors thrown will be swallowed.
6469
@@ -68,7 +73,7 @@ def error(self, hook_context: HookContext, exception: Exception, hints: dict):
6873
"""
6974
pass
7075

71-
def finally_after(self, hook_context: HookContext, hints: dict):
76+
def finally_after(self, hook_context: HookContext, hints: dict) -> None:
7277
"""
7378
Run after flag evaluation, including any error processing.
7479
This will always run. Errors will be swallowed.

openfeature/hook/hook_support.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def error_hooks(
1313
exception: Exception,
1414
hooks: typing.List[Hook],
1515
hints: typing.Optional[typing.Mapping] = None,
16-
):
16+
) -> None:
1717
kwargs = {"hook_context": hook_context, "exception": exception, "hints": hints}
1818
_execute_hooks(
1919
flag_type=flag_type, hooks=hooks, hook_method=HookType.ERROR, **kwargs
@@ -25,7 +25,7 @@ def after_all_hooks(
2525
hook_context: HookContext,
2626
hooks: typing.List[Hook],
2727
hints: typing.Optional[typing.Mapping] = None,
28-
):
28+
) -> None:
2929
kwargs = {"hook_context": hook_context, "hints": hints}
3030
_execute_hooks(
3131
flag_type=flag_type, hooks=hooks, hook_method=HookType.FINALLY_AFTER, **kwargs
@@ -35,10 +35,10 @@ def after_all_hooks(
3535
def after_hooks(
3636
flag_type: FlagType,
3737
hook_context: HookContext,
38-
details: FlagEvaluationDetails,
38+
details: FlagEvaluationDetails[typing.Any],
3939
hooks: typing.List[Hook],
4040
hints: typing.Optional[typing.Mapping] = None,
41-
):
41+
) -> None:
4242
kwargs = {"hook_context": hook_context, "details": details, "hints": hints}
4343
_execute_hooks_unchecked(
4444
flag_type=flag_type, hooks=hooks, hook_method=HookType.AFTER, **kwargs
@@ -55,7 +55,7 @@ def before_hooks(
5555
executed_hooks = _execute_hooks_unchecked(
5656
flag_type=flag_type, hooks=hooks, hook_method=HookType.BEFORE, **kwargs
5757
)
58-
filtered_hooks = list(filter(lambda hook: hook is not None, executed_hooks))
58+
filtered_hooks = [result for result in executed_hooks if result is not None]
5959

6060
if filtered_hooks:
6161
return reduce(lambda a, b: a.merge(b), filtered_hooks)
@@ -64,7 +64,10 @@ def before_hooks(
6464

6565

6666
def _execute_hooks(
67-
flag_type: FlagType, hooks: typing.List[Hook], hook_method: HookType, **kwargs
67+
flag_type: FlagType,
68+
hooks: typing.List[Hook],
69+
hook_method: HookType,
70+
**kwargs: typing.Any,
6871
) -> list:
6972
"""
7073
Run multiple hooks of any hook type. All of these hooks will be run through an
@@ -84,8 +87,11 @@ def _execute_hooks(
8487

8588

8689
def _execute_hooks_unchecked(
87-
flag_type: FlagType, hooks, hook_method: HookType, **kwargs
88-
) -> list:
90+
flag_type: FlagType,
91+
hooks: typing.List[Hook],
92+
hook_method: HookType,
93+
**kwargs: typing.Any,
94+
) -> typing.List[typing.Optional[EvaluationContext]]:
8995
"""
9096
Execute a single hook without checking whether an exception is thrown. This is
9197
used in the before and after hooks since any exception will be caught in the
@@ -104,7 +110,9 @@ def _execute_hooks_unchecked(
104110
]
105111

106112

107-
def _execute_hook_checked(hook: Hook, hook_method: HookType, **kwargs):
113+
def _execute_hook_checked(
114+
hook: Hook, hook_method: HookType, **kwargs: typing.Any
115+
) -> typing.Optional[EvaluationContext]:
108116
"""
109117
Try and run a single hook and catch any exception thrown. This is used in the
110118
after all and error hooks since any error thrown at this point needs to be caught.
@@ -115,6 +123,10 @@ def _execute_hook_checked(hook: Hook, hook_method: HookType, **kwargs):
115123
:return: the result of the hook method
116124
"""
117125
try:
118-
return getattr(hook, hook_method.value)(**kwargs)
126+
return typing.cast(
127+
"typing.Optional[EvaluationContext]",
128+
getattr(hook, hook_method.value)(**kwargs),
129+
)
119130
except Exception: # pragma: no cover
120131
logging.error(f"Exception when running {hook_method.value} hooks")
132+
return None

openfeature/provider/in_memory_provider.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class State(StrEnum):
3232
state: State = State.ENABLED
3333
context_evaluator: typing.Optional[
3434
typing.Callable[
35-
["InMemoryFlag", EvaluationContext], FlagResolutionDetails[T_co]
35+
["InMemoryFlag[T_co]", EvaluationContext], FlagResolutionDetails[T_co]
3636
]
3737
] = None
3838

@@ -52,15 +52,15 @@ def resolve(
5252
)
5353

5454

55-
FlagStorage = typing.Dict[str, InMemoryFlag]
55+
FlagStorage = typing.Dict[str, InMemoryFlag[typing.Any]]
5656

5757
V = typing.TypeVar("V")
5858

5959

6060
class InMemoryProvider(AbstractProvider):
6161
_flags: FlagStorage
6262

63-
def __init__(self, flags: FlagStorage):
63+
def __init__(self, flags: FlagStorage) -> None:
6464
self._flags = flags.copy()
6565

6666
def get_metadata(self) -> Metadata:

openfeature/provider/provider.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99

1010
class AbstractProvider:
11-
def initialize(self, evaluation_context: EvaluationContext):
11+
def initialize(self, evaluation_context: EvaluationContext) -> None:
1212
pass
1313

14-
def shutdown(self):
14+
def shutdown(self) -> None:
1515
pass
1616

1717
@abstractmethod

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ Homepage = "https://github.com/open-feature/python-sdk"
2929
files = "openfeature"
3030
namespace_packages = true
3131
explicit_package_bases = true
32+
pretty = true
33+
strict = true
34+
disallow_any_generics = false
3235

3336
[tool.ruff]
3437
exclude = [

0 commit comments

Comments
 (0)