Skip to content

use py310 style union annotations #421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fastcore/_nbdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"type_hints": "01_basics.ipynb",
"annotations": "01_basics.ipynb",
"anno_ret": "01_basics.ipynb",
"union2tuple": "01_basics.ipynb",
"argnames": "01_basics.ipynb",
"with_cast": "01_basics.ipynb",
"store_attr": "01_basics.ipynb",
Expand Down
24 changes: 16 additions & 8 deletions fastcore/basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
'null', 'tonull', 'get_class', 'mk_class', 'wrap_class', 'ignore_exceptions', 'exec_local', 'risinstance',
'Inf', 'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'in_',
'true', 'stop', 'gen', 'chunked', 'otherwise', 'custom_dir', 'AttrDict', 'get_annotations_ex', 'eval_type',
'type_hints', 'annotations', 'anno_ret', 'argnames', 'with_cast', 'store_attr', 'attrdict', 'properties',
'camel2words', 'camel2snake', 'snake2camel', 'class2attr', 'getcallable', 'getattrs', 'hasattrs', 'setattrs',
'try_attrs', 'GetAttrBase', 'GetAttr', 'delegate_attr', 'ShowPrint', 'Int', 'Str', 'Float', 'flatten',
'concat', 'strcat', 'detuplify', 'replicate', 'setify', 'merge', 'range_of', 'groupby', 'last_index',
'filter_dict', 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', 'not_', 'argwhere',
'filter_ex', 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_callable', 'nested_idx',
'type_hints', 'annotations', 'anno_ret', 'union2tuple', 'argnames', 'with_cast', 'store_attr', 'attrdict',
'properties', 'camel2words', 'camel2snake', 'snake2camel', 'class2attr', 'getcallable', 'getattrs',
'hasattrs', 'setattrs', 'try_attrs', 'GetAttrBase', 'GetAttr', 'delegate_attr', 'ShowPrint', 'Int', 'Str',
'Float', 'flatten', 'concat', 'strcat', 'detuplify', 'replicate', 'setify', 'merge', 'range_of', 'groupby',
'last_index', 'filter_dict', 'filter_keys', 'filter_values', 'cycle', 'zip_cycle', 'sorted_ex', 'not_',
'argwhere', 'filter_ex', 'range_of', 'renumerate', 'first', 'nested_attr', 'nested_callable', 'nested_idx',
'set_nested_idx', 'val2idx', 'uniqueify', 'loop_first_last', 'loop_first', 'loop_last', 'num_methods',
'rnum_methods', 'inum_methods', 'fastuple', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'bind', 'mapt', 'map_ex',
'compose', 'maps', 'partialler', 'instantiate', 'using_attr', 'Self', 'Self', 'copy_func', 'patch_to',
Expand All @@ -20,6 +20,8 @@
from .imports import *
import builtins,types
import pprint
try: from types import UnionType
except ImportError: UnionType = None

# Cell
defaults = SimpleNamespace()
Expand Down Expand Up @@ -289,7 +291,7 @@ def get_annotations_ex(obj, *, globals=None, locals=None):
def eval_type(t, glb, loc):
"`eval` a type or collection of types, if needed, for annotations in py3.10+"
if isinstance(t,str):
if '|' in t: return eval_type(tuple(t.split('|')), glb, loc)
if '|' in t: return Union[eval_type(tuple(t.split('|')), glb, loc)]
return eval(t, glb, loc)
if isinstance(t,(tuple,list)): return type(t)([eval_type(c, glb, loc) for c in t])
return t
Expand Down Expand Up @@ -320,6 +322,12 @@ def anno_ret(func):
"Get the return annotation of `func`"
return annotations(func).get('return', None) if func else None

# Cell
def union2tuple(t):
if (getattr(t, '__origin__', None) is Union
or (UnionType and isinstance(t, UnionType))): return t.__args__
return t

# Cell
def argnames(f, frame=False):
"Names of arguments to function or frame `f`"
Expand Down Expand Up @@ -906,7 +914,7 @@ def patch(f=None, *, as_prop=False, cls_method=False):
"Decorator: add `f` to the first parameter's class (based on f's type annotations)"
if f is None: return partial(patch, as_prop=as_prop, cls_method=cls_method)
ann,glb,loc = get_annotations_ex(f)
cls = eval_type(ann.pop('cls') if cls_method else next(iter(ann.values())), glb, loc)
cls = union2tuple(eval_type(ann.pop('cls') if cls_method else next(iter(ann.values())), glb, loc))
return patch_to(cls, as_prop=as_prop, cls_method=cls_method)(f)

# Cell
Expand Down
2 changes: 1 addition & 1 deletion fastcore/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def _reset(self):

def add(self, t, f):
"Add type `t` and function `f`"
if not isinstance(t,tuple): t=tuple(L(t))
if not isinstance(t, tuple): t = tuple(L(union2tuple(t)))
for t_ in t: self.d[t_] = f
self._reset()

Expand Down
2 changes: 1 addition & 1 deletion fastcore/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from operator import itemgetter,attrgetter
from warnings import warn
from typing import Iterable,Generator,Sequence,Iterator,List,Set,Dict,Union,Optional
from typing import Iterable,Generator,Sequence,Iterator,List,Set,Dict,Union,Optional,Tuple
from functools import partial,reduce
from pathlib import Path

Expand Down
2 changes: 1 addition & 1 deletion fastcore/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __init__(self, enc=None, dec=None, split_idx=None, order=None):
if enc:
self.encodes.add(enc)
self.order = getattr(enc,'order',self.order)
if len(type_hints(enc)) > 0: self.input_types = first(type_hints(enc).values())
if len(type_hints(enc)) > 0: self.input_types = union2tuple(first(type_hints(enc).values()))
self._name = _get_name(enc)
if dec: self.decodes.add(dec)

Expand Down
4 changes: 2 additions & 2 deletions fastcore/xtras.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def __setstate__(self, s): self.coll,self.idxs,self.cache,self.tfm = s['coll'],s

# Cell
def walk(
path:(Path,str), # path to start searching
path:Path|str, # path to start searching
symlinks:bool=True, # follow symlinks?
keep_file:callable=noop, # function that returns True for wanted files
keep_folder:callable=noop, # function that returns True for folders to enter
Expand All @@ -118,7 +118,7 @@ def walk(

# Cell
def globtastic(
path:Union[Path,str], # path to start searching
path:Path|str, # path to start searching
recursive:bool=True, # search subfolders
symlinks:bool=True, # follow symlinks?
file_glob:str=None, # Only include files matching glob
Expand Down
44 changes: 36 additions & 8 deletions nbs/01_basics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"#export\n",
"from fastcore.imports import *\n",
"import builtins,types\n",
"import pprint"
"import pprint\n",
"try: from types import UnionType\n",
"except ImportError: UnionType = None"
]
},
{
Expand Down Expand Up @@ -840,7 +842,7 @@
"text/markdown": [
"<h4 id=\"noops\" class=\"doc_header\"><code>noops</code><a href=\"https://github.com/fastai/fastcore/tree/master/fastcore/imports.py#L39\" class=\"source_link\" style=\"float:right\">[source]</a></h4>\n",
"\n",
"> <code>noops</code>(**`x`**=*`None`*, **\\*`args`**, **\\*\\*`kwargs`**)\n",
"> <code>noops</code>(**`self`**, **`x`**=*`None`*, **\\*`args`**, **\\*\\*`kwargs`**)\n",
"\n",
"Do nothing (method)"
],
Expand Down Expand Up @@ -1447,7 +1449,7 @@
"def eval_type(t, glb, loc):\n",
" \"`eval` a type or collection of types, if needed, for annotations in py3.10+\"\n",
" if isinstance(t,str):\n",
" if '|' in t: return eval_type(tuple(t.split('|')), glb, loc)\n",
" if '|' in t: return Union[eval_type(tuple(t.split('|')), glb, loc)]\n",
" return eval(t, glb, loc)\n",
" if isinstance(t,(tuple,list)): return type(t)([eval_type(c, glb, loc) for c in t])\n",
" return t"
Expand Down Expand Up @@ -1488,7 +1490,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"`|` is supported in types when using `eval_type` even for python versions prior to 3.9, by returning a tuple of types:"
"`|` is supported for defining `Union` types when using `eval_type` even for python versions prior to 3.9:"
]
},
{
Expand All @@ -1499,7 +1501,7 @@
{
"data": {
"text/plain": [
"(__main__._T2a, __main__._T2b)"
"typing.Union[__main__._T2a, __main__._T2b]"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1701,6 +1703,32 @@
"test_eq(anno_ret(None), None) # instead of passing in a func, pass in None"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#export\n",
"def union2tuple(t):\n",
" if (getattr(t, '__origin__', None) is Union\n",
" or (UnionType and isinstance(t, UnionType))): return t.__args__\n",
" return t"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"test_eq(union2tuple(Union[int,str]), (int,str))\n",
"test_eq(union2tuple(int), int)\n",
"test_eq(union2tuple(Tuple[int,str]), Tuple[int,str])\n",
"test_eq(union2tuple((int,str)), (int,str))\n",
"if UnionType: test_eq(union2tuple(int|str), (int,str))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -4850,7 +4878,7 @@
" \"Decorator: add `f` to the first parameter's class (based on f's type annotations)\"\n",
" if f is None: return partial(patch, as_prop=as_prop, cls_method=cls_method)\n",
" ann,glb,loc = get_annotations_ex(f)\n",
" cls = eval_type(ann.pop('cls') if cls_method else next(iter(ann.values())), glb, loc)\n",
" cls = union2tuple(eval_type(ann.pop('cls') if cls_method else next(iter(ann.values())), glb, loc))\n",
" return patch_to(cls, as_prop=as_prop, cls_method=cls_method)(f)"
]
},
Expand Down Expand Up @@ -5377,7 +5405,7 @@
{
"data": {
"text/plain": [
"8"
"4"
]
},
"execution_count": null,
Expand Down Expand Up @@ -5504,7 +5532,7 @@
"metadata": {},
"outputs": [],
"source": [
"def discount(price:(int,float), pct:float): \n",
"def discount(price:int|float, pct:float): \n",
" return (1-pct) * price\n",
"\n",
"assert 90.0 == discount(100.0, .1)"
Expand Down
26 changes: 13 additions & 13 deletions nbs/03_xtras.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@
{
"data": {
"text/plain": [
"['b', 'f', 'a', 'c', 'g', 'd', 'e', 'h']"
"['f', 'b', 'a', 'c', 'h', 'd', 'g', 'e']"
]
},
"execution_count": null,
Expand Down Expand Up @@ -630,7 +630,7 @@
"source": [
"#export\n",
"def walk(\n",
" path:(Path,str), # path to start searching\n",
" path:Path|str, # path to start searching\n",
" symlinks:bool=True, # follow symlinks?\n",
" keep_file:callable=noop, # function that returns True for wanted files\n",
" keep_folder:callable=noop, # function that returns True for folders to enter\n",
Expand All @@ -651,7 +651,7 @@
"source": [
"#export\n",
"def globtastic(\n",
" path:Union[Path,str], # path to start searching\n",
" path:Path|str, # path to start searching\n",
" recursive:bool=True, # search subfolders\n",
" symlinks:bool=True, # follow symlinks?\n",
" file_glob:str=None, # Only include files matching glob\n",
Expand Down Expand Up @@ -686,7 +686,7 @@
{
"data": {
"text/plain": [
"(#9) ['./06_docments.ipynb','./08_script.ipynb','./04_dispatch.ipynb','./01_basics.ipynb','./fastcore/docments.py','./fastcore/docscrape.py','./fastcore/script.py','./fastcore/basics.py','./fastcore/dispatch.py']"
"(#9) ['./08_script.ipynb','./04_dispatch.ipynb','./06_docments.ipynb','./01_basics.ipynb','./fastcore/docments.py','./fastcore/dispatch.py','./fastcore/basics.py','./fastcore/docscrape.py','./fastcore/script.py']"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1193,7 +1193,7 @@
{
"data": {
"text/plain": [
"'pip 22.0.3 from /opt/conda/lib/python3.9/site-packages/pip (python 3.9)'"
"'pip 22.1.2 from /Users/seem/.pyenv/versions/3.9.7/lib/python3.9/site-packages/pip (python 3.9)'"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1395,7 +1395,7 @@
{
"data": {
"text/plain": [
"Path('/home/jovyan/local/zach/fastcore/fastcore')"
"Path('/Users/seem/code/fastcore/fastcore')"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1461,7 +1461,7 @@
{
"data": {
"text/plain": [
"Path('05_transform.ipynb')"
"Path('parallel_test.py')"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1495,7 +1495,7 @@
{
"data": {
"text/plain": [
"(Path('../fastcore/shutil.py'), Path('05_transform.ipynb'))"
"(Path('../fastcore/shutil.py'), Path('parallel_win.ipynb'))"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1661,7 +1661,7 @@
{
"data": {
"text/plain": [
"'nbformat/converter.py#L10'"
"'nbformat/converter.py#L11'"
]
},
"execution_count": null,
Expand Down Expand Up @@ -1879,8 +1879,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Num Events: 8, Freq/sec: 386.5\n",
"Most recent: ▇▇▁▁▁ 315.1 316.3 246.7 262.8 264.1\n"
"Num Events: 1, Freq/sec: 728.9\n",
"Most recent: ▇▅▂▅▁ 338.0 292.7 266.0 299.7 228.3\n"
]
}
],
Expand Down Expand Up @@ -2029,7 +2029,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"2000-01-01 12:00:00 UTC is 2000-01-01 12:00:00+00:00 local time\n"
"2000-01-01 12:00:00 UTC is 2000-01-01 23:00:00+11:00 local time\n"
]
}
],
Expand Down Expand Up @@ -2059,7 +2059,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"2000-01-01 12:00:00 local is 2000-01-01 12:00:00+00:00 UTC time\n"
"2000-01-01 12:00:00 local is 2000-01-01 01:00:00+00:00 UTC time\n"
]
}
],
Expand Down
6 changes: 3 additions & 3 deletions nbs/04_dispatch.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@
"\n",
" def add(self, t, f):\n",
" \"Add type `t` and function `f`\"\n",
" if not isinstance(t,tuple): t=tuple(L(t))\n",
" if not isinstance(t, tuple): t = tuple(L(union2tuple(t)))\n",
" for t_ in t: self.d[t_] = f\n",
" self._reset()\n",
"\n",
Expand Down Expand Up @@ -363,7 +363,7 @@
"def f2(x:int, y:float): return x+y #int and float for 2nd arg\n",
"def f_nin(x:numbers.Integral)->int: return x+1 #integral numeric\n",
"def f_ni2(x:int): return x #integer\n",
"def f_bll(x:(bool,list)): return x #bool or list\n",
"def f_bll(x:bool|list): return x #bool or list\n",
"def f_num(x:numbers.Number): return x #Number (root of numerics) "
]
},
Expand Down Expand Up @@ -949,7 +949,7 @@
"metadata": {},
"outputs": [],
"source": [
"def m_nin(self, x:(str,numbers.Integral)): return str(x)+'1'\n",
"def m_nin(self, x:str|numbers.Integral): return str(x)+'1'\n",
"def m_bll(self, x:bool): self.foo='a'\n",
"def m_num(self, x:numbers.Number): return x*2\n",
"\n",
Expand Down
15 changes: 9 additions & 6 deletions nbs/05_transform.ipynb

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions nbs/06_docments.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,13 @@
"from nbdev.export import notebook2script\n",
"notebook2script()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand Down