does anno_dict
do anything more or new compared with __annotations__
?
from fastcore.meta import *
from fastcore.test import *
import inspect
anno_dict
docs
inspect.getdoc(anno_dict)
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist"
I have to confess I don’t undersatnd the docs statement very well. So, I look into the source code of anno_dict
and empty2none
.
print(inspect.getsource(anno_dict))
def anno_dict(f):
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist"
return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
print(inspect.getsource(empty2none))
def empty2none(p):
"Replace `Parameter.empty` with `None`"
return None if p==inspect.Parameter.empty else p
Dive in
If a parameter’s default value is Parameter.empty
, then empty2none
is to replace Parameter.empty
with None
. So, I think it is reasonable to assume p
is primarily used as a parameter’s default value. The cell below supports this assumption.
def foo(a, b:int=1): pass
sig = inspect.signature(foo)
for k,v in sig.parameters.items():
print(f'{k} is a parameter {v}, whose default value is {v.default}, \
if apply empty2none to default value, then the default value is {empty2none(v.default)}')
print(f'{k} is a parameter {v}, whose default value is {v.default}, \
if apply empty2none to parameter, then we get: {empty2none(v)}')
a is a parameter a, whose default value is <class 'inspect._empty'>, if apply empty2none to default value, then the default value is None
a is a parameter a, whose default value is <class 'inspect._empty'>, if apply empty2none to parameter, then we get: a
b is a parameter b: int = 1, whose default value is 1, if apply empty2none to default value, then the default value is 1
b is a parameter b: int = 1, whose default value is 1, if apply empty2none to parameter, then we get: b: int = 1
So, what is odd is that in anno_dict
, empty2none
is applied to v
which is not parameter’s default value, but mostly classes like int
, list
ect, as in __annotations__
.
Then I experimented the section below and didn’t find anno_dict
doing anything new than __annotations__
.
anno_dict
seems not add anything new to __annotations__
def foo(a, b:int=1): pass
test_eq(foo.__annotations__, {'b': int})
test_eq(anno_dict(foo), {'b': int})
def foo(a:bool, b:int=1): pass
test_eq(foo.__annotations__, {'a': bool, 'b': int})
test_eq(anno_dict(foo), {'a': bool, 'b': int})
def foo(a, d:list, b:int=1, c:bool=True): pass
test_eq(foo.__annotations__, {'d': list, 'b': int, 'c': bool})
test_eq(anno_dict(foo), {'d': list, 'b': int, 'c': bool})
from fastcore.foundation import L
def foo(a, b): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
def _f(a:int, b:L)->str: ...
test_eq(_f.__annotations__, {'a': int, 'b': L, 'return': str})
test_eq(anno_dict(_f), {'a': int, 'b': L, 'return': str})
Question! so far above anno_dict has done nothing new or more, so what am I missing here?
Does fastcore want anno_dict to include params with no annos?
If so, I have written a lengthy anno_dict_maybe
to do it.
def anno_dict_maybe(f):
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist"
new_anno = {}
for k, v in inspect.signature(f).parameters.items():
if k not in f.__annotations__:
new_anno[k] = None
else:
new_anno[k] = f.__annotations__[k]
if 'return' in f.__annotations__:
new_anno['return'] = f.__annotations__['return']
if True in [bool(v) for k,v in new_anno.items()]:
return new_anno
else:
return {}
def foo(a:int, b, c:bool=True)->str: pass
test_eq(foo.__annotations__, {'a': int, 'c': bool, 'return': str})
test_eq(anno_dict(foo), {'a': int, 'c': bool, 'return': str})
test_eq(anno_dict_maybe(foo), {'a': int, 'b': None, 'c': bool, 'return': str})
def foo(a, b, c): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
test_eq(anno_dict_maybe(foo), {})