Skip to content

Commit

Permalink
fix: Type annotation None now correctly supported with value-check …
Browse files Browse the repository at this point in the history
…and `"null"` (#1429)

Resolves #1423

- `"null"` is helpful if you build requests in JS have `null` values
- If a annotation is missing, it's `param.empty` but this is difference
to an explicit set `None` type annotation and needs to processed
separately as `empty`
- The other _"simple types"_ do a type-cast and would raise a
`ValueError` in case of invalid data, but `None` was always taken,
regardless of the value?!

Here some examples of definition which had some issues before and are
now working as expected:
```py
    @Exposed
    def my_method(
        self,
        *,
        test0: None | db.Key | str = None,
        test1: None = None,
        test2: None | str = None,
        test3: str | None = None,
        test4=None,
        test5: int = None,
    ):
``` 

Of course `test1: None = None` is a little bit impractical, but since
it's a valid syntax it should also work in this way and accept only
`None`, `"None"` or `"null"`. But `test4` has no type annotation and
accepts any value as-is.

Providing `"None"` for `test2` would result into `None`, while `"None"`
for `test3` would result into `"None"` (due to the order).

However, my initially problem parameter `test0: None | db.Key | str =
None,` is now working as expected: I can provide explicit `"None"` or
`"null"` and it's no longer always `None`.
  • Loading branch information
sveneberth authored Feb 27, 2025
1 parent 206d6cd commit f1aaf8b
Showing 1 changed file with 7 additions and 5 deletions.
12 changes: 7 additions & 5 deletions src/viur/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t
return float(value)
elif annotation is bool:
return utils.parse.bool(value)
elif annotation is types.NoneType:
return None
elif annotation is types.NoneType or annotation is None:
if value in (None, "None", "null"):
return None
raise ValueError(f"Expected None for parameter {name}. Got: {value!r}")

# complex types
origin_type = t.get_origin(annotation)
Expand Down Expand Up @@ -157,7 +159,7 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t
if self._instance and i == 0 and param_name == "self":
continue

param_type = param.annotation if param.annotation is not param.empty else None
param_type = param.annotation
param_required = param.default is param.empty

# take positional parameters first
Expand All @@ -168,7 +170,7 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t
try:
value = next(args_iter)

if param_type:
if param_type is not param.empty:
value = parse_value_by_annotation(param_type, param_name, value)

parsed_args.append(value)
Expand All @@ -186,7 +188,7 @@ def parse_value_by_annotation(annotation: type, name: str, value: str | list | t
):
value = kwargs.pop(param_name)

if param_type:
if param_type is not param.empty:
value = parse_value_by_annotation(param_type, param_name, value)

parsed_kwargs[param_name] = value
Expand Down

0 comments on commit f1aaf8b

Please sign in to comment.