Skip to content

Commit f1aeb7d

Browse files
author
Izaak Gough
committed
feat: improve internal typings in manifest
1 parent 534b3b0 commit f1aeb7d

1 file changed

Lines changed: 48 additions & 14 deletions

File tree

src/firebase_functions/private/manifest.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,41 @@
1919

2020
import dataclasses as _dataclasses
2121
import typing as _typing
22+
from collections.abc import Mapping as _Mapping
23+
from collections.abc import Sequence as _Sequence
2224
from enum import Enum as _Enum
2325

2426
import typing_extensions as _typing_extensions
2527

2628
import firebase_functions.params as _params
2729
import firebase_functions.private.util as _util
2830

31+
ManifestParamBase = _params.Param | _params.SecretParam
32+
33+
ManifestParam = (
34+
_params.BoolParam
35+
| _params.IntParam
36+
| _params._FloatParam
37+
| _params.ListParam
38+
| _params.SecretParam
39+
| _params.StringParam
40+
)
41+
42+
SpecValue: _typing.TypeAlias = (
43+
str
44+
| int
45+
| float
46+
| bool
47+
| _util.Sentinel
48+
| list["SpecValue"]
49+
| dict[str, "SpecValue"]
50+
| None
51+
)
52+
53+
54+
class _DataclassInstance(_typing.Protocol):
55+
__dataclass_fields__: _typing.ClassVar[dict[str, _dataclasses.Field[object]]]
56+
2957

3058
class SecretEnvironmentVariable(_typing.TypedDict):
3159
key: _typing_extensions.Required[str]
@@ -180,7 +208,7 @@ class ManifestRequiredApi(_typing.TypedDict):
180208
class ManifestStack:
181209
endpoints: dict[str, ManifestEndpoint]
182210
specVersion: str = "v1alpha1"
183-
params: list[_typing.Any] | None = _dataclasses.field(default_factory=list[_typing.Any])
211+
params: _Sequence[ManifestParamBase] | None = _dataclasses.field(default_factory=list[ManifestParamBase])
184212
requiredAPIs: list[ManifestRequiredApi] = _dataclasses.field(
185213
default_factory=list[ManifestRequiredApi]
186214
)
@@ -191,7 +219,7 @@ def _param_input_to_spec(
191219
| _params.ResourceInput
192220
| _params.SelectInput
193221
| _params.MultiSelectInput,
194-
) -> dict[str, _typing.Any]:
222+
) -> dict[str, SpecValue]:
195223
if isinstance(param_input, _params.TextInput):
196224
return {
197225
"text": {
@@ -233,8 +261,8 @@ def _param_input_to_spec(
233261
return {}
234262

235263

236-
def _param_to_spec(param: _params.Param | _params.SecretParam) -> dict[str, _typing.Any]:
237-
spec_dict: dict[str, _typing.Any] = {
264+
def _param_to_spec(param: ManifestParamBase) -> dict[str, SpecValue]:
265+
spec_dict: dict[str, SpecValue] = {
238266
"name": param.name,
239267
"label": param.label,
240268
"description": param.description,
@@ -266,45 +294,51 @@ def _param_to_spec(param: _params.Param | _params.SecretParam) -> dict[str, _typ
266294
return _dict_to_spec(spec_dict)
267295

268296

269-
def _object_to_spec(data) -> object:
297+
def _object_to_spec(data: object) -> SpecValue:
270298
if isinstance(data, _Enum):
271299
return data.value
272300
elif isinstance(data, _params.Expression):
273301
return f"{data}"
274302
elif _dataclasses.is_dataclass(data):
275-
return _dataclass_to_spec(data)
303+
return _dataclass_to_spec(_typing.cast(_DataclassInstance, data))
276304
elif isinstance(data, list):
277305
return list(map(_object_to_spec, data))
278306
elif isinstance(data, dict):
279307
return _dict_to_spec(data)
280-
else:
308+
elif data is None:
309+
return None
310+
elif isinstance(data, _util.Sentinel):
281311
return data
312+
elif isinstance(data, (str, int, float, bool)):
313+
return data
314+
else:
315+
raise TypeError(f"Unsupported manifest spec value: {type(data)!r}")
282316

283317

284-
def _dict_factory(data: list[tuple[str, _typing.Any]]) -> dict:
285-
out: dict = {}
318+
def _dict_factory(data: list[tuple[str, object]]) -> dict[str, SpecValue]:
319+
out: dict[str, SpecValue] = {}
286320
for key, value in data:
287321
if value is not None:
288322
out[key] = _object_to_spec(value)
289323
return out
290324

291325

292-
def _dataclass_to_spec(data) -> dict:
293-
out: dict = {}
326+
def _dataclass_to_spec(data: _DataclassInstance) -> dict[str, SpecValue]:
327+
out: dict[str, SpecValue] = {}
294328
for field in _dataclasses.fields(data):
295329
value = _object_to_spec(getattr(data, field.name))
296330
if value is not None:
297331
out[field.name] = value
298332
return out
299333

300334

301-
def _dict_to_spec(data: dict) -> dict:
335+
def _dict_to_spec(data: _Mapping[str, object]) -> dict[str, SpecValue]:
302336
return _dict_factory(list(data.items()))
303337

304338

305-
def manifest_to_spec_dict(manifest: ManifestStack) -> dict:
339+
def manifest_to_spec_dict(manifest: ManifestStack) -> dict[str, SpecValue]:
306340
params = manifest.params
307-
out: dict = _dataclass_to_spec(manifest)
341+
out: dict[str, SpecValue] = _dataclass_to_spec(manifest)
308342
if params is not None:
309343
out["params"] = list(map(_param_to_spec, params))
310344
return out

0 commit comments

Comments
 (0)