Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a field is typing.Optional

What is the best way to check if a field from a class is typing.Optional?

Example code:

from typing import Optional
import re
from dataclasses import dataclass, fields

@dataclass(frozen=True)
class TestClass:
    required_field_1: str
    required_field_2: int
    optional_field: Optional[str]

def get_all_optional_fields(fields) -> list:
    return [field.name for field in fields if __is_optional_field(field)]

def __is_optional_field(field) -> bool:
    regex = '^typing.Union\[.*, NoneType\]$'
    return re.match(regex, str(field.type)) is not None

print(get_all_optional_fields(fields(TestClass)))

Where fields is from dataclasses, I wanna list all the Optional fields. What I'm doing at this moment to solve it, is using a Regex-based on the field name, but I don't like this approach. Is there a better way of doing it?

like image 711
Rodrigo Farias Rezino Avatar asked Jul 01 '19 09:07

Rodrigo Farias Rezino


1 Answers

For reference, Python 3.8 (first released October 2019) added get_origin and get_args functions to the typing module.

Examples from the docs:

assert get_origin(Dict[str, int]) is dict
assert get_args(Dict[int, str]) == (int, str)

assert get_origin(Union[int, str]) is Union
assert get_args(Union[int, str]) == (int, str)

This will allow:

def is_optional(field):
    return typing.get_origin(field) is Union and \
           type(None) in typing.get_args(field)

For older Pythons, here is some compatibility code:

# Python >= 3.8
try:
    from typing import Literal, get_args, get_origin
# Compatibility
except ImportError:
    get_args = lambda t: getattr(t, '__args__', ()) \
                         if t is not Generic else Generic
    get_origin = lambda t: getattr(t, '__origin__', None)
like image 190
Garrett Avatar answered Oct 24 '22 03:10

Garrett