Skip to content
This repository was archived by the owner on Feb 2, 2024. It is now read-only.

automatic generation of type checks for overload - #911

Open
vlad-perevezentsev wants to merge 12 commits into
IntelPython:numba_typingfrom
vlad-perevezentsev:numba_typing
Open

automatic generation of type checks for overload#911
vlad-perevezentsev wants to merge 12 commits into
IntelPython:numba_typingfrom
vlad-perevezentsev:numba_typing

Conversation

@vlad-perevezentsev

Copy link
Copy Markdown

No description provided.

@pep8speaks

pep8speaks commented Aug 17, 2020

Copy link
Copy Markdown

Hello @vlad-perevezentsev! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻

Comment last updated at 2020-09-29 14:36:35 UTC

Comment threadnumba_typing/overload_list.py Outdated
return self._types_dict[p_type](self, p_type, n_type)
return self._types_dict[p_type](n_type)
except KeyError:
print((f'A check for the {p_type} was not found. {n_type}'))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we will (implicitly) return None from match in this case, is this intended behavior? Please add explicit return or throw

Comment threadnumba_typing/overload_list.py Outdated

def choose_func_by_sig(sig_list, values_dict, defaults_dict={}):
checker = TypeChecker()
for sig in sig_list: # sig = (Signature,func)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of accessing sig[0] and sig[1] each time you can write for sig, func in sig_list:

Comment threadnumba_typing/overload_list.py Outdated
Comment on lines +1 to +12
import numpy
import numba
from numba import types
from numba import typeof
from numba.extending import overload
from type_annotations import product_annotations, get_func_annotations
from numba import njit
import typing
from numba import NumbaDeprecationWarning, NumbaPendingDeprecationWarning
import warnings
from numba.typed import List, Dict
from inspect import getfullargspec

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some unused imports here, like numpy, typeof and njit, please clean them up.

Comment threadnumba_typing/overload_list.py Outdated
Comment on lines +15 to +16
warnings.simplefilter('ignore', category=NumbaDeprecationWarning)
warnings.simplefilter('ignore', category=NumbaPendingDeprecationWarning)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

Comment threadnumba_typing/overload_list.py Outdated

def check_tuple_type(self, p_type, n_type):
res = False
if isinstance(n_type, types.Tuple):

@AlexanderKalistratovAlexanderKalistratovSep 9, 2020

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using n_type.key you could use n_type.types which is defined for both Tuple and UniTuple, so you can unite both branches:

Something like:

ifnotisinstance(n_type, types.Tuple, types.UniTuple):
returnFalseforp_val, n_valinzip(p_type.__args__, n_type.types):
ifnotself.match(p_val, n_val):
returnFalsereturnTrue

And btw you need to check that size of p_type.__args__ and n_type.types are the same.
And I believe you don't have tests for the case when they are different.

Comment threadnumba_typing/overload_list.py Outdated
return self._types_dict[p_type](self, p_type, n_type)
return self._types_dict[p_type](n_type)
except KeyError:
print((f'A check for the {p_type} was not found.'))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rise an exception

Comment threadnumba_typing/overload_list.py Outdated

def match(self, p_type, n_type):
try:
if p_type == typing.Any:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it like this:

ifp_type==typing.Any:
returnTrueifself._is_generic(p_type):
origin_type=self._get_origin(p_type)
iforigin_type==typing.Generic:
returnself.match_generic(p_type, n_type)
returnself._types_dict[origin_type](self, p_type, n_type)
ifisinstance(p_type, typing.TypeVar):
returnself.match_typevar(p_type, n_type)
ifp_typein (list, tuple):
returnself._types_dict[p_type](self, p_type, n_type)
returnself._types_dict[p_type](n_type)

Comment threadnumba_typing/overload_list.py Outdated
return None

def match_typevar(self, p_type, n_type):
if not self._typevars_dict.get(p_type) and n_type not in self._typevars_dict.values():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need condition n_type not in self._typevars_dict.values() ?

if not self._typevars_dict.get(p_type) and n_type not in self._typevars_dict.values():
self._typevars_dict[p_type] = n_type
return True
return self._typevars_dict.get(p_type) == n_type

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be self.match. E.g. list and 'types.List' are synonyms but will fail equality check ('list != types.List').
And I'm assuming you don't have such tests?

Comment threadnumba_typing/overload_list.py Outdated
def match_generic(self, p_type, n_type):
res = True
for arg in p_type.__args__:
res = res and self.match(arg, n_type)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's doesn't feel right. Do we have any test for this case?

Comment threadnumba_typing/overload_list.py Outdated

if sig.defaults.get(name, False):
full_match = full_match and sig.defaults[name] == typ.literal_value
else:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this else?

Comment threadnumba_typing/overload_list.py Outdated


def check_list_type(self, p_type, n_type):
res = isinstance(n_type, types.List) or isinstance(n_type, types.ListType)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

res=isinstance(n_type, (types.List, types.ListType))

Comment threadnumba_typing/overload_list.py Outdated

def check_list_type(self, p_type, n_type):
res = isinstance(n_type, types.List) or isinstance(n_type, types.ListType)
if isinstance(p_type, type):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(p_type, (list, typing.List))

?

Comment threadnumba_typing/overload_list.py Outdated
elif isinstance(p_type, typing.TypeVar):
return self.match_typevar(p_type, n_type)
else:
if p_type in (list, tuple):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't dict be here too?

Comment threadnumba_typing/overload_list.py Outdated

class TypeChecker:

_types_dict = {int: check_int_type, float: check_float_type, bool: check_bool_type,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this checks to be added using add_type_check function

@AlexanderKalistratov

Copy link
Copy Markdown
Collaborator

https://github.com/IntelPython/sdc/blob/numba_typing/numba_typing/local_variable_type_annotations.py#L11

This code actually modifies original function globals. This shouldn't happen, please fix.

Comment threadnumba_typing/overload_list.py Outdated
TypeChecker.add_type_check(dict, check_dict_type)


def choose_func_by_sig(sig_list, values_dict, defaults_dict):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it this way:

defchoose_func_by_sig(sig_list, values_dict, defaults_dict):
defcheck_signature(sig_params, types_dict):
checker=TypeChecker()
forname, typintypes_dict.items(): # name,type = 'a',int64ifisinstance(typ, types.Literal):
typ=typ.literal_typeifnotchecker.match(sig_params[name], typ):
returnFalsereturnTrueforsig, funcinsig_list: # sig = (Signature,func)forparaminsig.parameters: # param = {'a':int,'b':int}ifcheck_signature(param, values_dict):
returnfuncreturnNone

Comment threadnumba_typing/test_overload_list.py Outdated
return typ.__name__
return typ

value_keys = ", ".join("{}".format(key) for key in values_dict.keys())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use f'{key}' instead. Here and everywhere

Comment threadnumba_typing/test_overload_list.py Outdated
D_list['qwe'] = List([3, 4, 5])
str_1 = 'qwe'
str_2 = 'qaz'
test_cases = [('int', [1, 2], {'a': int, 'b': int}), ('float', [1.0, 2.0], {'a': float, 'b': float}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{'a': int, 'b': int} why do you need two parameters of the same type?
What are you actually testing?

Comment threadnumba_typing/test_overload_list.py Outdated
str_2 = 'qaz'
test_cases = [('int', [1, 2], {'a': int, 'b': int}), ('float', [1.0, 2.0], {'a': float, 'b': float}),
('bool', [True, True], {'a': bool, 'b': bool}), ('str', ['str_1', 'str_2'], {'a': str, 'b': str}),
('list', [[1, 2], [3, 4]], {'a': typing.List[int], 'b':list}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need second list parameter?

Comment threadnumba_typing/test_overload_list.py Outdated
('tuple', [(1, 2.0), ('3', False)], {'a': typing.Tuple[int, float], 'b':tuple}),
('dict', ['D', 'D_1'], {'a': typing.Dict[str, int], 'b': typing.Dict[int, bool]}),
('union_1', [1, False], {'a': typing.Union[int, str], 'b': typing.Union[float, bool]}),
('union_2', ['str_1', False], {'a': typing.Union[int, str], 'b': typing.Union[float, bool]}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And why you are not testing second Union parameter?

Comment threadnumba_typing/test_overload_list.py Outdated
('TypeVar_ListT_DictK_ListT', ['L', 'D_list'], {'a': 'typing.List[T]',
'b': 'typing.Dict[K, typing.List[T]]'})]

test_cases_default = [('int_defaults', [1], {'a': int}, {'b': 0}), ('float_defaults', [1.0], {'a': float}, {'b': 0.0}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what about both - annotation and default value?

Comment threadnumba_typing/test_overload_list.py Outdated
('TypeVar_ListT_T', ['L', 5], {'a': 'typing.List[T]', 'b': 'T'}),
('TypeVar_ListT_DictKT', ['L', 'D'], {'a': 'typing.List[T]', 'b': 'typing.Dict[K, T]'}),
('TypeVar_ListT_DictK_ListT', ['L', 'D_list'], {'a': 'typing.List[T]',
'b': 'typing.Dict[K, typing.List[T]]'})]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are where any tests for TypeVar with specified types restriction?

Comment threadnumba_typing/test_overload_list.py Outdated
('union_2', ['str_1', False], {'a': typing.Union[int, str], 'b': typing.Union[float, bool]}),
('nested_list', ['L_int', 'L_float'], {'a': typing.List[typing.List[int]],
'b': typing.List[typing.List[typing.List[float]]]}),
('TypeVar_TT', ['L_f', [3.0, 4.0]], {'a': 'T', 'b': 'T'}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any negative tests for such case?

Comment threadnumba_typing/test_overload_list.py Outdated
('tuple_defaults', [(1, 2)], {'a': tuple}, {'b': (0, 0)})]


for name, val, annotation in test_cases:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer you to group all cases into 4-5 tests with subtests. Something like this:

deftest_common_types():
test_cases= [({'a': 0}, {'a': int}),
({'a': 0.}, {'a': float}),
...]
forcaseintest_cases:
withself.Subtest(case=case):
run_test(case)

Comment threadnumba_typing/overload_list.py Outdated
result = choose_func_by_sig(sig_list, values_dict)

if result is None:
raise TypeError(f'Unsupported types a={a}, b={b}')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks both a and b are undefined.

Comment threadnumba_typing/overload_list.py Outdated

for sig, _ in list_signature:
for param in sig.parameters:
if len(param) != len(values_dict.items()):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to call method items() here.

Comment threadnumba_typing/overload_list.py Outdated
for name, val in defaults_dict.items():
if sig_def.get(name) is None:
raise AttributeError(f'{name} does not match the signature of the function passed to overload_list')
if not sig_def[name] == val:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition looks pretty strange. Maybe if sig_def[name] != val?

@kozlov-alexeykozlov-alexey added this to the gold milestone Feb 24, 2021
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@vlad-perevezentsev@pep8speaks@AlexanderKalistratov@Hardcode84@densmirn@kozlov-alexey