from fastcore.meta import *
from fastcore.test import *
import inspect00_quesolved_anno_dict
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 Ldef 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?
use fastdebug to double check
from fastdebug.utils import *
from fastdebug.core import *
from fastcore.meta import *fdb = Fastdb(anno_dict)
fdb.eg = """
def foo(a, b): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
from fastcore.foundation import L
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})
"""fdb.snoop(['empty2none(v)'])09:48:27.11 >>> Call to anno_dict in File "/tmp/anno_dict.py", line 3
09:48:27.11 ...... f = <function foo>
09:48:27.11 3 | def anno_dict(f):
09:48:27.11 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.11 Dict comprehension:
09:48:27.11 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.11 .......... Iterating over <dict_itemiterator object>
09:48:27.11 Result: {}
09:48:27.11 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.12 <<< Return value from anno_dict: {}
09:48:27.12 >>> Call to anno_dict in File "/tmp/anno_dict.py", line 3
09:48:27.12 ...... f = <function _f>
09:48:27.12 3 | def anno_dict(f):
09:48:27.12 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.12 Dict comprehension:
09:48:27.12 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.12 .......... Iterating over <dict_itemiterator object>
09:48:27.12 .......... Values of k: 'a', 'b', 'return'
09:48:27.12 .......... Values of v: <class 'int'>, <class 'fastcore.foundation.L'>, <class 'str'>
09:48:27.12 .......... Values of empty2none(v): <class 'int'>, <class 'fastcore.foundation.L'>, <class 'str'>
09:48:27.12 Result: {'a': <class 'int'>, 'b': <class 'fastcore.foundation.L'>, 'return': <class 'str'>}
09:48:27.12 5 | return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}
09:48:27.12 <<< Return value from anno_dict: {'a': <class 'int'>, 'b': <class 'fastcore.foundation.L'>, 'return': <class 'str'>}
======================================================== Investigating anno_dict =========================================================
============================================================== on line None ==============================================================
with example
def foo(a, b): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
from fastcore.foundation import L
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})
fdb.docsrc(1, "empty2none works on paramter.default especially when the default is Parameter.empty; anno_dict works on the types \
of params, not the value of params; so it is odd to use empty2none in anno_dict;")======================================================== Investigating anno_dict =========================================================
=============================================================== on line 1 ================================================================
with example
def foo(a, b): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
from fastcore.foundation import L
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})
print selected srcline with expands below--------
def anno_dict(f): (0)
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist"==========================================================(1)
empty2none works on paramter.default especially when the default is Parameter.empty; anno_dict works on the types of params, not the value of params; so it is odd to use empty2none in anno_dict;
return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()} (2)
(3)
Review srcode with all comments added so far======================================================================================================
def anno_dict(f):=========================================================================(0)
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist" # empty2none works on paramter.default especially when the default is Parameter.empty; anno_dict works on the types of params, not the value of params; so it is odd to use empty2none in anno_dict;; (1)
return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}=========(2)
(3)
part No.1 out of 1 parts
fdb.print()======================================================== Investigating anno_dict =========================================================
=============================================================== on line 1 ================================================================
with example
def foo(a, b): pass
test_eq(foo.__annotations__, {})
test_eq(anno_dict(foo), {})
from fastcore.foundation import L
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})
def anno_dict(f):=========================================================================(0)
"`__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist" # empty2none works on paramter.default especially when the default is Parameter.empty; anno_dict works on the types of params, not the value of params; so it is odd to use empty2none in anno_dict;; (1)
return {k:empty2none(v) for k,v in getattr(f, '__annotations__', {}).items()}=========(2)
(3)
Does fastcore want anno_dict to include params with no annos?
If so, I have written a lengthy anno_dict_maybe to do it. (can be shorter if needed)
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 hasattr(f, '__annotations__'):
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: passtest_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): passtest_eq(foo.__annotations__, {})test_eq(anno_dict(foo), {})test_eq(anno_dict_maybe(foo), {})Jeremy’s response
A supportive and confirmative response from Jeremy on this issue