Skip to content

Commit

Permalink
enhance optional-parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ric-evans committed Sep 13, 2024
1 parent 7748725 commit cc7e5c0
Showing 1 changed file with 31 additions and 16 deletions.
47 changes: 31 additions & 16 deletions wipac_dev_tools/enviro_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,24 +282,39 @@ def __repr__(self) -> str:
)


def _extract_optional(
typ: GenericAlias,
) -> Union[GenericAlias, types.GenericAlias, None]:
# Optional[bool] *is* typing.Union[bool, NoneType]
# similarly...
# Optional[bool]
# Union[bool, None]
# Union[None, bool]
# bool | None
# None | bool
if (
typ.__origin__ == Union
and len(typ.__args__) == 2
and type(None) in typ.__args__ # doesn't matter where None is
):
return next(x for x in typ.__args__ if x is not type(None))
else:
return None


def _extract_final(typ: GenericAlias) -> Union[GenericAlias, types.GenericAlias, None]:
if typ.__origin__ == Final:
return typ.__args__[0]
else:
return None


def deconstruct_typehint(
field: dataclasses.Field,
) -> Tuple[type, Optional[Tuple[type, ...]]]:
"""Take a type hint and return its type and its arguments' types."""
typ, arg_typs = field.type, None

# some helper functions
def _is_optional(typ: GenericAlias) -> bool:
# Optional[int] *is* typing.Union[int, NoneType]
return (
typ.__origin__ == Union
and len(typ.__args__) == 2
and typ.__args__[-1] == type(None) # noqa: E721
)

def _is_final(typ: GenericAlias) -> bool:
return bool(typ.__origin__ == Final)

# detect bare 'Final' and 'Optional'
if isinstance(typ, _SpecialForm):
raise ValueError(
Expand All @@ -312,12 +327,12 @@ def _is_final(typ: GenericAlias) -> bool:
# typing.GenericAlias -> Dict, List, ...
# types.GenericAlias -> dict[int,str], list[bool], ...
if isinstance(typ, (GenericAlias, types.GenericAlias)):
if _is_optional(typ) or _is_final(typ):
if (inner := _extract_optional(typ)) or (inner := _extract_final(typ)):
# Ex: Final[int], Optional[Dict[str,int]]
if isinstance(typ.__args__[0], type): # Ex: Final[int], Optional[int]
typ, arg_typs = typ.__args__[0], None
if isinstance(inner, type): # Ex: Final[int], Optional[int]
typ, arg_typs = inner, None
else: # Final[Dict[str,int]], Optional[Dict[str,int]]
typ, arg_typs = typ.__args__[0].__origin__, typ.__args__[0].__args__
typ, arg_typs = inner.__origin__, inner.__args__
else:
# Ex:
# List[int] -> list, [int]
Expand Down

0 comments on commit cc7e5c0

Please sign in to comment.