From 0d0ed8cbe4e9c20c7d2cb522c57976b36304b12b Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Thu, 17 Nov 2022 16:20:12 -0800 Subject: [PATCH 01/10] Cleanup _libcall and _usercall --- onnxscript/values.py | 104 +++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 472dab2898..9420e7f870 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -183,77 +183,47 @@ def fun(*args, **kwargs): return fun + def wrap(self, inputs): + """Adapts inputs into representation used by onnxscript eager mode.""" + def adapt(x): + if isinstance(x, np.ndarray): + return tensor.Tensor(x) + elif isinstance(x, tensor.Tensor): + return x + elif isinstance(x, (bool, int, float)): + return tensor.Tensor(np.array(x)) + elif x is None: + return None + elif isinstance(x, list): + return [adapt(elt) for elt in x] + raise TypeError(f"Unexpected input type {type(x)}.") + return [adapt(x) for x in inputs] + + def unwrap(self, output): + """Unwraps Tensor wrapper around numpy arrays.""" + if isinstance(output, tensor.Tensor): + return output.value + elif output is None: + return None + elif isinstance(output, list): + return [self.unwrap(elt) for elt in output] + elif isinstance(output, tuple): + return tuple([self.unwrap(elt) for elt in output]) + elif isinstance(output, np.ndarray): + return output + raise TypeError(f"Unexpected type {type(output)}.") + def __call__(self, *args, **kwargs): """Implements an eager-mode execution of an onnxscript function.""" + libcall = False if len(args) == 0: # Operator Constant, it is usually called within a function. - return self._libcall(**kwargs) - if isinstance(args[0], tensor.Tensor): - return self._libcall(*args, **kwargs) - return self._usercall(*args, **kwargs) - - def _usercall(self, *args, **kwargs): - """Eager mode""" - new_args = [] - for i, a in enumerate(args): - if isinstance(a, np.ndarray): - new_args.append(tensor.Tensor(a)) - elif isinstance(a, (bool, int, float)): - new_args.append(tensor.Tensor(np.array(a))) - else: - raise TypeError(f"Unexpected input type {type(a)} for an input {i}.") - res = self.function(*new_args, **kwargs) - if isinstance(res, np.ndarray): - return res - if isinstance(res, tensor.Tensor): - return res.value - if isinstance(res, (list, tuple)): - unwrapped = [] - for i, r in enumerate(res): - if isinstance(r, np.ndarray): - unwrapped.append(r) - elif isinstance(r, tensor.Tensor): - unwrapped.append(r.value) - else: - raise TypeError( - f"Unexpected output type {type(r)} for an output {i} " - f"in function {self.function!r}." - ) - if isinstance(res, tuple): - return tuple(unwrapped) - return unwrapped - raise TypeError(f"Unexpected output type {type(res)} in function {self.function!r}.") - - def _libcall(self, *args, **kwargs): - """This method must be called when a function decoracted with `script` - calls another one decorated with `script`. - """ - new_args = [] - for i, a in enumerate(args): - if isinstance(a, tensor.Tensor): - new_args.append(a) - elif isinstance(a, bool): - # TODO: default values for function parameters - # are not properly handled yet. This section - # should disappear. - new_args.append(tensor.Tensor(np.array(a))) - else: - raise TypeError(f"Unexpected input type {type(a)} for an input {i}.") - res = self.function(*new_args, **kwargs) - if isinstance(res, tensor.Tensor): - return res - if isinstance(res, tuple): - unwrapped = [] - for i, r in enumerate(res): - if isinstance(r, tensor.Tensor): - unwrapped.append(r) - else: - raise TypeError( - f"Unexpected output type {type(r)} for an output {i} " - f"in function {self.function!r}." - ) - return tuple(unwrapped) - raise TypeError(f"Unexpected output type {type(res)} in function {self.function!r}.") + libcall = True + elif isinstance(args[0], tensor.Tensor): + libcall = True + new_args = self.wrap(args) + result = self.function(*new_args, **kwargs) + return result if libcall else self.unwrap(result) def to_function_proto(self, domain=None): """Converts the function into :class:`onnx.FunctionProto`.""" From 22c6e78f41f9d3f395f84190786642f682315540 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Thu, 17 Nov 2022 16:43:23 -0800 Subject: [PATCH 02/10] Minor fixes --- onnxscript/values.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 9420e7f870..df03cc0730 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -183,21 +183,26 @@ def fun(*args, **kwargs): return fun - def wrap(self, inputs): - """Adapts inputs into representation used by onnxscript eager mode.""" - def adapt(x): - if isinstance(x, np.ndarray): - return tensor.Tensor(x) - elif isinstance(x, tensor.Tensor): - return x - elif isinstance(x, (bool, int, float)): - return tensor.Tensor(np.array(x)) - elif x is None: - return None - elif isinstance(x, list): - return [adapt(elt) for elt in x] - raise TypeError(f"Unexpected input type {type(x)}.") - return [adapt(x) for x in inputs] + def wrap(self, input): + """Adapts inputs into representation used by onnxscript eager mode. + + This primarily adds an onnxscript Tensor wrapper around numpy arrays. + But it also provides promotion of scalars into tensors as a convenience. + Need to revisit whether this secondary convenience feature is worthwhile. + """ + if isinstance(input, np.ndarray): + return tensor.Tensor(input) + elif isinstance(input, tensor.Tensor): + return input + elif isinstance(input, (bool, int, float)): + return tensor.Tensor(np.array(input)) + elif input is None: + return None + elif isinstance(input, list): + return [self.wrap(elt) for elt in input] + elif isinstance(input, tuple): + return tuple([self.wrap(elt) for elt in input]) + raise TypeError(f"Unexpected input type {type(input)}.") def unwrap(self, output): """Unwraps Tensor wrapper around numpy arrays.""" From e2265b57290626f5939c7ba7f7e839b717f2b119 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Thu, 17 Nov 2022 22:30:41 -0800 Subject: [PATCH 03/10] Fix logic to determine when to unwrap --- onnxscript/values.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index df03cc0730..26128932f7 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -183,26 +183,32 @@ def fun(*args, **kwargs): return fun - def wrap(self, input): + def wrap(self, inputs): """Adapts inputs into representation used by onnxscript eager mode. This primarily adds an onnxscript Tensor wrapper around numpy arrays. But it also provides promotion of scalars into tensors as a convenience. Need to revisit whether this secondary convenience feature is worthwhile. """ - if isinstance(input, np.ndarray): - return tensor.Tensor(input) - elif isinstance(input, tensor.Tensor): - return input - elif isinstance(input, (bool, int, float)): - return tensor.Tensor(np.array(input)) - elif input is None: - return None - elif isinstance(input, list): - return [self.wrap(elt) for elt in input] - elif isinstance(input, tuple): - return tuple([self.wrap(elt) for elt in input]) - raise TypeError(f"Unexpected input type {type(input)}.") + has_array = False + def do_unwrap(input): + if isinstance(input, np.ndarray): + nonlocal has_array + has_array = True + return tensor.Tensor(input) + elif isinstance(input, tensor.Tensor): + return input + elif isinstance(input, (bool, int, float)): + return tensor.Tensor(np.array(input)) + elif input is None: + return None + elif isinstance(input, list): + return [do_unwrap for elt in input] + elif isinstance(input, tuple): + return tuple([do_unwrap(elt) for elt in input]) + raise TypeError(f"Unexpected input type {type(input)}.") + result = do_unwrap(inputs) + return result, has_array def unwrap(self, output): """Unwraps Tensor wrapper around numpy arrays.""" @@ -220,15 +226,9 @@ def unwrap(self, output): def __call__(self, *args, **kwargs): """Implements an eager-mode execution of an onnxscript function.""" - libcall = False - if len(args) == 0: - # Operator Constant, it is usually called within a function. - libcall = True - elif isinstance(args[0], tensor.Tensor): - libcall = True - new_args = self.wrap(args) + new_args, has_array = self.wrap(args) result = self.function(*new_args, **kwargs) - return result if libcall else self.unwrap(result) + return self.unwrap(result) if has_array else result def to_function_proto(self, domain=None): """Converts the function into :class:`onnx.FunctionProto`.""" From e4099f7265e5d2a5fb6810b118a1ad652b05e8e1 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Fri, 18 Nov 2022 13:24:10 -0800 Subject: [PATCH 04/10] Add comments --- onnxscript/values.py | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 26128932f7..9dc7038bde 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -185,13 +185,25 @@ def fun(*args, **kwargs): def wrap(self, inputs): """Adapts inputs into representation used by onnxscript eager mode. - - This primarily adds an onnxscript Tensor wrapper around numpy arrays. - But it also provides promotion of scalars into tensors as a convenience. - Need to revisit whether this secondary convenience feature is worthwhile. + + This does the following transformations: + * It adds an onnxscript Tensor wrapper around numpy arrays, which + allows the use of overloaded operators like + to be controlled by onnxscript. + * It also provides a promotion of scalars into tensors as a convenience. + This is needed to complement the similar promotion supported by the + onnxscript converter (for example, when an attribute is promoted and used + as an input argument). + + Args: + inputs: a list/tuple of inputs to an ONNX function + + Returns: + a pair (wrapped_inputs, flag) where flag indicates whether any numpy array + was wrapped into a Tensor. """ has_array = False - def do_unwrap(input): + + def do_wrap(input): if isinstance(input, np.ndarray): nonlocal has_array has_array = True @@ -203,15 +215,24 @@ def do_unwrap(input): elif input is None: return None elif isinstance(input, list): - return [do_unwrap for elt in input] + return [do_wrap for elt in input] elif isinstance(input, tuple): - return tuple([do_unwrap(elt) for elt in input]) + return tuple([do_wrap(elt) for elt in input]) raise TypeError(f"Unexpected input type {type(input)}.") - result = do_unwrap(inputs) + + result = do_wrap(inputs) return result, has_array def unwrap(self, output): - """Unwraps Tensor wrapper around numpy arrays.""" + """Unwraps Tensor wrapper around numpy arrays. + + Args: + output: output of an ONNX function, which can be either a single + onnx value or a list/tuple of onnx values. + + Returns: + unwrapped output + """ if isinstance(output, tensor.Tensor): return output.value elif output is None: From b470e95581861921acbd2972d7355a4425d53358 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Fri, 18 Nov 2022 17:14:34 -0800 Subject: [PATCH 05/10] Address PR comments --- onnxscript/values.py | 138 +++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 9dc7038bde..64e3d73550 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -142,6 +142,70 @@ class OnnxClosure: function: Any +def adapt_to_eager_mode(inputs): + """Adapts inputs into representation used by onnxscript eager mode. + + This does the following transformations: + * It adds an onnxscript Tensor wrapper around numpy arrays, which + allows the use of overloaded operators like + to be controlled by onnxscript. + * It also provides a promotion of scalars into tensors as a convenience. + This is needed to complement the similar promotion supported by the + onnxscript converter (for example, when an attribute is promoted and used + as an input argument). + + Args: + inputs: a list/tuple of inputs to an ONNX function + + Returns: + a pair (wrapped_inputs, flag) where flag indicates whether any numpy array + was wrapped into a Tensor. + """ + has_array = False + + def adapt(input): + if isinstance(input, np.ndarray): + nonlocal has_array + has_array = True + return tensor.Tensor(input) + elif isinstance(input, tensor.Tensor): + return input + elif isinstance(input, (bool, int, float)): + return tensor.Tensor(np.array(input)) + elif input is None: + return None + elif isinstance(input, list): + return [adapt(elt) for elt in input] + elif isinstance(input, tuple): + return tuple(adapt(elt) for elt in input) + raise TypeError(f"Unexpected input type {type(input)}.") + + result = adapt(inputs) + return result, has_array + + +def adapt_to_user_mode(output): + """Unwraps Tensor wrapper around numpy arrays. + + Args: + output: output of an ONNX function, which can be either a single + onnx value or a list/tuple of onnx values. + + Returns: + unwrapped output + """ + if isinstance(output, tensor.Tensor): + return output.value + elif output is None: + return None + elif isinstance(output, list): + return [adapt_to_user_mode(elt) for elt in output] + elif isinstance(output, tuple): + return tuple(adapt_to_user_mode(elt) for elt in output) + elif isinstance(output, np.ndarray): + return output + raise TypeError(f"Unexpected type {type(output)}.") + + class OnnxFunction(Op): """Represents an ONNX op for which a function-body has been defined in onnxscript. @@ -183,73 +247,19 @@ def fun(*args, **kwargs): return fun - def wrap(self, inputs): - """Adapts inputs into representation used by onnxscript eager mode. - - This does the following transformations: - * It adds an onnxscript Tensor wrapper around numpy arrays, which - allows the use of overloaded operators like + to be controlled by onnxscript. - * It also provides a promotion of scalars into tensors as a convenience. - This is needed to complement the similar promotion supported by the - onnxscript converter (for example, when an attribute is promoted and used - as an input argument). - - Args: - inputs: a list/tuple of inputs to an ONNX function - - Returns: - a pair (wrapped_inputs, flag) where flag indicates whether any numpy array - was wrapped into a Tensor. - """ - has_array = False - - def do_wrap(input): - if isinstance(input, np.ndarray): - nonlocal has_array - has_array = True - return tensor.Tensor(input) - elif isinstance(input, tensor.Tensor): - return input - elif isinstance(input, (bool, int, float)): - return tensor.Tensor(np.array(input)) - elif input is None: - return None - elif isinstance(input, list): - return [do_wrap for elt in input] - elif isinstance(input, tuple): - return tuple([do_wrap(elt) for elt in input]) - raise TypeError(f"Unexpected input type {type(input)}.") - - result = do_wrap(inputs) - return result, has_array - - def unwrap(self, output): - """Unwraps Tensor wrapper around numpy arrays. - - Args: - output: output of an ONNX function, which can be either a single - onnx value or a list/tuple of onnx values. - - Returns: - unwrapped output - """ - if isinstance(output, tensor.Tensor): - return output.value - elif output is None: - return None - elif isinstance(output, list): - return [self.unwrap(elt) for elt in output] - elif isinstance(output, tuple): - return tuple([self.unwrap(elt) for elt in output]) - elif isinstance(output, np.ndarray): - return output - raise TypeError(f"Unexpected type {type(output)}.") - def __call__(self, *args, **kwargs): """Implements an eager-mode execution of an onnxscript function.""" - new_args, has_array = self.wrap(args) + new_args, has_array = adapt_to_eager_mode(args) result = self.function(*new_args, **kwargs) - return self.unwrap(result) if has_array else result + + # We use a heuristic to decide whether to return output values as + # numpy arrays or tensor.Tensors. If the function has at least one + # numpy array as input, we return numpy arrays. Otherwise, we return + # tensor.Tensors. We could use a user-specified flag to control this + # or explicitly track whether this is a top-level function-call or + # a nested function-call. + + return adapt_to_user_mode(result) if has_array else result def to_function_proto(self, domain=None): """Converts the function into :class:`onnx.FunctionProto`.""" From d3f51ce4b0c58f95cb0dc3d36d04271db8ce1f38 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Mon, 21 Nov 2022 14:54:32 -0800 Subject: [PATCH 06/10] Rename local method --- onnxscript/values.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 64e3d73550..d224165b02 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -141,8 +141,9 @@ class OnnxClosure: function: Any +# EagerModeInput = tensor.Tensor | list['EagerModeInput'] | tuple['EagerModeInput', ...] | None -def adapt_to_eager_mode(inputs): +def _adapt_to_eager_mode(inputs): """Adapts inputs into representation used by onnxscript eager mode. This does the following transformations: @@ -183,7 +184,7 @@ def adapt(input): return result, has_array -def adapt_to_user_mode(output): +def _adapt_to_user_mode(output): """Unwraps Tensor wrapper around numpy arrays. Args: @@ -198,9 +199,9 @@ def adapt_to_user_mode(output): elif output is None: return None elif isinstance(output, list): - return [adapt_to_user_mode(elt) for elt in output] + return [_adapt_to_user_mode(elt) for elt in output] elif isinstance(output, tuple): - return tuple(adapt_to_user_mode(elt) for elt in output) + return tuple(_adapt_to_user_mode(elt) for elt in output) elif isinstance(output, np.ndarray): return output raise TypeError(f"Unexpected type {type(output)}.") @@ -249,7 +250,7 @@ def fun(*args, **kwargs): def __call__(self, *args, **kwargs): """Implements an eager-mode execution of an onnxscript function.""" - new_args, has_array = adapt_to_eager_mode(args) + new_args, has_array = _adapt_to_eager_mode(args) result = self.function(*new_args, **kwargs) # We use a heuristic to decide whether to return output values as @@ -259,7 +260,7 @@ def __call__(self, *args, **kwargs): # or explicitly track whether this is a top-level function-call or # a nested function-call. - return adapt_to_user_mode(result) if has_array else result + return _adapt_to_user_mode(result) if has_array else result def to_function_proto(self, domain=None): """Converts the function into :class:`onnx.FunctionProto`.""" From da6d1def9c2532fc418bb19850d2db8afcb40b11 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Mon, 21 Nov 2022 18:56:16 -0800 Subject: [PATCH 07/10] Add type annotation --- onnxscript/values.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index d224165b02..8051167859 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -8,7 +8,7 @@ import logging import types from enum import IntFlag -from typing import Any, _GenericAlias # type: ignore[attr-defined] +from typing import Any, _GenericAlias, Optional, Union # type: ignore[attr-defined] import numpy as np import onnx @@ -141,9 +141,27 @@ class OnnxClosure: function: Any -# EagerModeInput = tensor.Tensor | list['EagerModeInput'] | tuple['EagerModeInput', ...] | None -def _adapt_to_eager_mode(inputs): +UserModeValue = Union[ + Optional[np.ndarray], list["UserModeValue"], tuple["UserModeValue", ...] +] + +EagerModeValue = Union[ + Optional["tensor.Tensor"], list["EagerModeValue"], tuple["EagerModeValue", ...] +] + +ExtendedModeValue = Union[ + Optional["tensor.Tensor"], + list["ExtendedModeValue"], + tuple["ExtendedModeValue", ...], + np.ndarray, + int, + float, + bool, +] + + +def _adapt_to_eager_mode(inputs: ExtendedModeValue) -> EagerModeValue: """Adapts inputs into representation used by onnxscript eager mode. This does the following transformations: @@ -163,7 +181,7 @@ def _adapt_to_eager_mode(inputs): """ has_array = False - def adapt(input): + def adapt(input: ExtendedModeValue) -> EagerModeValue: if isinstance(input, np.ndarray): nonlocal has_array has_array = True @@ -184,7 +202,7 @@ def adapt(input): return result, has_array -def _adapt_to_user_mode(output): +def _adapt_to_user_mode(output: ExtendedModeValue) -> UserModeValue: """Unwraps Tensor wrapper around numpy arrays. Args: From ec5e431b9738e8bb6df9120f3af2222a290d26f8 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Tue, 22 Nov 2022 12:24:53 -0800 Subject: [PATCH 08/10] Fix generic type annotation --- onnxscript/values.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 8051167859..e0613b15fd 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -8,7 +8,14 @@ import logging import types from enum import IntFlag -from typing import Any, _GenericAlias, Optional, Union # type: ignore[attr-defined] +from typing import ( # type: ignore[attr-defined] + Any, + List, + Optional, + Tuple, + Union, + _GenericAlias, +) import numpy as np import onnx @@ -142,18 +149,16 @@ class OnnxClosure: function: Any -UserModeValue = Union[ - Optional[np.ndarray], list["UserModeValue"], tuple["UserModeValue", ...] -] +UserModeValue = Union[Optional[np.ndarray], List["UserModeValue"], Tuple["UserModeValue", ...]] EagerModeValue = Union[ - Optional["tensor.Tensor"], list["EagerModeValue"], tuple["EagerModeValue", ...] + Optional["tensor.Tensor"], List["EagerModeValue"], Tuple["EagerModeValue", ...] ] ExtendedModeValue = Union[ Optional["tensor.Tensor"], - list["ExtendedModeValue"], - tuple["ExtendedModeValue", ...], + List["ExtendedModeValue"], + Tuple["ExtendedModeValue", ...], np.ndarray, int, float, From f73c235305bfd4f0400fb07c3e5f1c89c6400eb6 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Tue, 22 Nov 2022 12:50:38 -0800 Subject: [PATCH 09/10] Fix typing --- onnxscript/values.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index e0613b15fd..854b030788 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -8,14 +8,7 @@ import logging import types from enum import IntFlag -from typing import ( # type: ignore[attr-defined] - Any, - List, - Optional, - Tuple, - Union, - _GenericAlias, -) +from typing import Any, _GenericAlias import numpy as np import onnx @@ -149,21 +142,25 @@ class OnnxClosure: function: Any -UserModeValue = Union[Optional[np.ndarray], List["UserModeValue"], Tuple["UserModeValue", ...]] +UserModeValue = Any +EagerModeValue = Any +ExtendedModeValue = Any -EagerModeValue = Union[ - Optional["tensor.Tensor"], List["EagerModeValue"], Tuple["EagerModeValue", ...] -] +# UserModeValue = Union[Optional[np.ndarray], List["UserModeValue"], Tuple["UserModeValue", ...]] -ExtendedModeValue = Union[ - Optional["tensor.Tensor"], - List["ExtendedModeValue"], - Tuple["ExtendedModeValue", ...], - np.ndarray, - int, - float, - bool, -] +# EagerModeValue = Union[ +# Optional["tensor.Tensor"], List["EagerModeValue"], Tuple["EagerModeValue", ...] +# ] + +# ExtendedModeValue = Union[ +# Optional["tensor.Tensor"], +# List["ExtendedModeValue"], +# Tuple["ExtendedModeValue", ...], +# np.ndarray, +# int, +# float, +# bool, +# ] def _adapt_to_eager_mode(inputs: ExtendedModeValue) -> EagerModeValue: From af44aec6659829f8557d09520f1508c97ef2d303 Mon Sep 17 00:00:00 2001 From: Ganesan Ramalingam Date: Tue, 22 Nov 2022 13:32:47 -0800 Subject: [PATCH 10/10] Fix deleted ignore --- onnxscript/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxscript/values.py b/onnxscript/values.py index 854b030788..ed919c99df 100644 --- a/onnxscript/values.py +++ b/onnxscript/values.py @@ -8,7 +8,7 @@ import logging import types from enum import IntFlag -from typing import Any, _GenericAlias +from typing import Any, _GenericAlias # type: ignore[attr-defined] import numpy as np import onnx