Repository: Quantco/spox (language: python)

Issue:
Caching for attributes that allow it
<!--
Thank you for the pull request.
Below are a few things we ask you kindly to self-check before getting a review. Remove checks that are not relevant.
-->

# Checklist

- [x] Added a `CHANGELOG.rst` entry



Discussion / hints:
@cbourjau does this look legit? How can I ensure correctness (up for a pair-testing session!)? I'm seeing ~2x speedup in an end-to-end test that constructs, builds, and serializes a graph.
> @cbourjau does this look legit? How can I ensure correctness (up for a pair-testing session!)? I'm seeing ~2x speedup in an end-to-end test that constructs, builds, and serializes a graph.

That sounds very promising and looks like a great start! Assuming that the user never mutates these private objects, which correctness issues do you anticipate? I am certainly happy to have a pair-programming session! 

If you are worried about correctness, it would be sufficient to check if the cached AttributeProto has the same attribute name as requested, and otherwise it can be copied and a new name can be set on the copy. You shouldn't see any copies, and if they are necessary in some corner cases, they will be observable as a slight performance regression (perhaps a warning can be added if wanted). Maybe something like this would work:
```py
         if self._last_onnx is None:
             self._last_onnx = self._to_onnx_deref(key)
         if self._last_onnx != key:
             proto = onnx.AttributeProto()
             proto.CopyFrom(self._last_onnx)
             proto.name = key
             self._last_onnx = proto
        return self._last_onnx
```

Why are the changes in `_standard` necessary? I'm not sure I can tell.
If `_Ref` is too annoying to handle (and I could see why that would be...) perhaps it should be removed, as we do not use functions anywhere (they are not that useful in the first place).


> Why are the changes in _standard necessary?

Right now we are creating new `Attr` objects with `type(v)(v.value)` which would strip the cache away.
> it would be sufficient to check if the cached AttributeProto has the same attribute name as requested, and otherwise it can be copied and a new name can be set on the copy.

The name/key is different when type checking:

https://github.com/Quantco/spox/blob/d59a9998c7158f00ec17334ad4d4210402e0095b/src/spox/_attributes.py#L53

As you can see, I'm completely ignoring it for now:

https://github.com/Quantco/spox/blob/0f109a17b10ccdf0c82f9c7d95d297986be46ebe/src/spox/_attributes.py#L61-L67

Do you see any concrete problems with this, and can we add a test?
Good point about the "dummy" keys used during the validation. The only worry I have about mutating the attribute is if its reference is hanging around elsewhere after a build call. Attributes will become part of Nodes when building. Thus, a test that reflects the real world would be if the attribute's name is updated in a `NodeProto` object:

```python
import spox
from onnx import AttributeProto
from onnx.helper import make_node

attr = spox._attributes.AttrFloat32s([1., 2.])
attr_onnx = attr._to_onnx("foo")

node = make_node("FooNode", inputs=["bar"], outputs=["baz"])
node.attribute.append(attr_onnx)

assert node.attribute[0].name == "foo"

attr_onnx.name = "ZZZ"
assert node.attribute[0].name == "foo"
```

The result is that `NodeProto.append` appears to make a copy so we are in the clear in this regard, but should add it as a test to catch any future breakage upstream. We may also add a stern warning to the docstring of `_to_onnx` that the return value of that function should not be assigned to anything long-lived and is primarily intended to be used immediately in `NodeProto.append` (which makes a copy as shown above).
For future reference: Simeon and I just discussed this issue. It is a bit scary to mutate the cached value with a user-provided name and it would be great to avoid doing that. To that end, we realized that the name is usually known from the start when creating the `Attrxxx` object in the opset. We can alter the opset generation to pass the key there and that should safe us from mutating it later. Only thing that may require a little more thought is where which name belongs for `_Ref` attributes. 

Fix patch:
diff --git a/src/spox/_attributes.py b/src/spox/_attributes.py
index 9034eb7f..8d02bcd7 100644
--- a/src/spox/_attributes.py
+++ b/src/spox/_attributes.py
@@ -32,18 +32,20 @@
 
 class Attr(ABC, Generic[T]):
     _value: Union[T, "_Ref[T]"]
+    _name: str
     _attribute_proto_type_int: ClassVar[int]
     _cached_onnx: Optional[AttributeProto]
 
-    def __init__(self, value: Union[T, "_Ref[T]"]):
+    def __init__(self, value: Union[T, "_Ref[T]"], name: str):
         self._value = value
+        self._name = name
         self._cached_onnx = None
 
         self._validate()
 
     @classmethod
-    def maybe(cls: Type[AttrT], value: Optional[T]) -> Optional[AttrT]:
-        return cls(value) if value is not None else None
+    def maybe(cls: Type[AttrT], value: Optional[T], name: str) -> Optional[AttrT]:
+        return cls(value, name) if value is not None else None
 
     @property
     def value(self) -> T:
@@ -53,20 +55,19 @@ def value(self) -> T:
         return self._value
 
     def _validate(self):
-        if self._to_onnx("dummy").type != self._attribute_proto_type_int:
+        if self._to_onnx().type != self._attribute_proto_type_int:
             raise TypeError(
                 f"Unable to instantiate `{type(self).__name__}` with value of type `{type(self.value).__name__}`."
             )
 
-    def _to_onnx(self, key: str) -> AttributeProto:
+    def _to_onnx(self) -> AttributeProto:
         if isinstance(self._value, _Ref):
-            return self._value._to_onnx(key)
+            return self._value._to_onnx()
         if self._cached_onnx is None:
-            self._cached_onnx = self._to_onnx_deref(key)
-        self._cached_onnx.name = key
+            self._cached_onnx = self._to_onnx_deref()
         return self._cached_onnx
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         """Conversion method for the dereferenced case."""
         raise NotImplementedError
 
@@ -81,58 +82,59 @@ class _Ref(Generic[T]):
 
     _concrete: Attr[T]
 
-    def __init__(self, concrete: Attr[T], outer_name: str):
+    def __init__(self, concrete: Attr[T], outer_name: str, name: str):
         self._concrete = concrete
         self._outer_name = outer_name
+        self._name = name
 
     def copy(self) -> "_Ref[T]":
         return self
 
-    def _to_onnx(self, key: str) -> AttributeProto:
-        parent_type = self._concrete._to_onnx(key).type
+    def _to_onnx(self) -> AttributeProto:
+        parent_type = self._concrete._to_onnx().type
         return AttributeProto(
-            name=key, ref_attr_name=self._outer_name, type=parent_type
+            name=self._name, ref_attr_name=self._outer_name, type=parent_type
         )
 
 
 class AttrFloat32(Attr[float]):
     _attribute_proto_type_int = AttributeProto.FLOAT
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         if isinstance(self.value, int):
-            return make_attribute(key, float(self.value))
-        return make_attribute(key, self.value)
+            return make_attribute(self._name, float(self.value))
+        return make_attribute(self._name, self.value)
 
 
 class AttrInt64(Attr[int]):
     _attribute_proto_type_int = AttributeProto.INT
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
-        return make_attribute(key, self.value)
+    def _to_onnx_deref(self) -> AttributeProto:
+        return make_attribute(self._name, self.value)
 
 
 class AttrString(Attr[str]):
     _attribute_proto_type_int = AttributeProto.STRING
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         # Strings are bytes on the onnx side
-        return make_attribute(key, self.value.encode())
+        return make_attribute(self._name, self.value.encode())
 
 
 class AttrTensor(Attr[np.ndarray]):
     _attribute_proto_type_int = AttributeProto.TENSOR
 
-    def __init__(self, value: Union[np.ndarray, _Ref[np.ndarray]]):
-        super().__init__(value.copy())
+    def __init__(self, value: Union[np.ndarray, _Ref[np.ndarray]], name: str):
+        super().__init__(value.copy(), name)
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
-        return make_attribute(key, from_array(self.value))
+    def _to_onnx_deref(self) -> AttributeProto:
+        return make_attribute(self._name, from_array(self.value))
 
 
 class AttrType(Attr[_type_system.Type]):
     _attribute_proto_type_int = AttributeProto.TYPE_PROTO
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         value = self.value  # for type-checkers with limited property support
         if isinstance(value, _type_system.Tensor):
             type_proto = make_tensor_type_proto(
@@ -145,7 +147,7 @@ def _to_onnx_deref(self, key: str) -> AttributeProto:
             type_proto = make_optional_type_proto(value.elem_type._to_onnx())
         else:
             raise NotImplementedError
-        return make_attribute(key, type_proto)
+        return make_attribute(self._name, type_proto)
 
 
 class AttrDtype(Attr[npt.DTypeLike]):
@@ -156,8 +158,8 @@ class AttrDtype(Attr[npt.DTypeLike]):
     def _validate(self):
         dtype_to_tensor_type(self.value)
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
-        return make_attribute(key, dtype_to_tensor_type(self.value))
+    def _to_onnx_deref(self) -> AttributeProto:
+        return make_attribute(self._name, dtype_to_tensor_type(self.value))
 
 
 class AttrGraph(Attr[Any]):
@@ -171,52 +173,56 @@ def _validate(self):
                 f"Expected value of type `spox.graph.Graph found `{type(self.value)}`"
             )
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         raise TypeError(
             "Graph attributes must be built using the `build_subgraph` callback in `Node.to_onnx`."
         )
 
 
 class _AttrIterable(Attr[Tuple[S, ...]], ABC):
-    def __init__(self, value: Union[Iterable[S], _Ref[Tuple[S, ...]]]):
-        super().__init__(value if isinstance(value, _Ref) else tuple(value))
+    def __init__(self, value: Union[Iterable[S], _Ref[Tuple[S, ...]]], name: str):
+        super().__init__(
+            value=value if isinstance(value, _Ref) else tuple(value), name=name
+        )
 
     @classmethod
     def maybe(
-        cls: Type[AttrIterableT], value: Optional[Iterable[S]]
+        cls: Type[AttrIterableT],
+        value: Optional[Iterable[S]],
+        name: str,
     ) -> Optional[AttrIterableT]:
-        return cls(tuple(value)) if value is not None else None
+        return cls(tuple(value), name) if value is not None else None
 
 
 class AttrFloat32s(_AttrIterable[float]):
     _attribute_proto_type_int = AttributeProto.FLOATS
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         # ensure values are all floats
         values = [float(v) for v in self.value]
-        return make_attribute(key, values)
+        return make_attribute(self._name, values)
 
 
 class AttrInt64s(_AttrIterable[int]):
     _attribute_proto_type_int = AttributeProto.INTS
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
-        return make_attribute(key, self.value)
+    def _to_onnx_deref(self) -> AttributeProto:
+        return make_attribute(self._name, self.value)
 
 
 class AttrStrings(_AttrIterable[str]):
     _attribute_proto_type_int = AttributeProto.STRINGS
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
-        return make_attribute(key, [v.encode() for v in self.value])
+    def _to_onnx_deref(self) -> AttributeProto:
+        return make_attribute(self._name, [v.encode() for v in self.value])
 
 
 class AttrTensors(_AttrIterable[np.ndarray]):
     _attribute_proto_type_int = AttributeProto.TENSORS
 
-    def _to_onnx_deref(self, key: str) -> AttributeProto:
+    def _to_onnx_deref(self) -> AttributeProto:
         tensors = [from_array(t) for t in self.value]
-        return make_attribute(key, tensors)
+        return make_attribute(self._name, tensors)
 
 
 def _deref(ref: _Ref[T]) -> T:
diff --git a/src/spox/_function.py b/src/spox/_function.py
index 39af7775..a2220611 100644
--- a/src/spox/_function.py
+++ b/src/spox/_function.py
@@ -39,7 +39,7 @@ class Function(_InternalNode):
     """
 
     func_args: Dict[str, Var]
-    func_attrs: Dict[str, _attributes._Ref]
+    func_attrs: Dict[str, _attributes.Attr]
     func_inputs: BaseInputs
     func_outputs: BaseOutputs
     func_graph: "_graph.Graph"
@@ -64,14 +64,13 @@ def infer_output_types(self) -> Dict[str, Type]:
             **{name: var.type for name, var in self.inputs.get_vars().items()}
         )
 
-        func_attrs = {}
+        self.func_attrs = {}
         for name, attr in self.attrs.get_fields().items():
             if attr is None:
                 raise TypeError(
                     f"Function attributes is not optional, but {name} is None."
                 )
-            func_attrs[name] = _attributes._Ref(concrete=attr, outer_name=name)
-        self.func_attrs = func_attrs
+            self.func_attrs[name] = attr
 
         self.func_inputs = self.Inputs(**self.func_args)  # type: ignore
         self.func_outputs = self.constructor(self.func_attrs, self.func_inputs)
diff --git a/src/spox/_graph.py b/src/spox/_graph.py
index 6d0426f3..436ab834 100644
--- a/src/spox/_graph.py
+++ b/src/spox/_graph.py
@@ -37,17 +37,23 @@ def arguments_dict(**kwargs: Optional[Union[Type, numpy.ndarray]]) -> Dict[str,
     """
     result = {}
     for name, info in kwargs.items():
-        attr_name = AttrString(name)
+        attr_name = AttrString(value=name, name="dummy")
         if isinstance(info, Type):
             result[name] = Argument(
-                Argument.Attributes(name=attr_name, type=AttrType(info), default=None),
+                Argument.Attributes(
+                    name=attr_name,
+                    type=AttrType(value=info, name="dummy"),
+                    default=None,
+                ),
                 BaseInputs(),
             ).outputs.arg
         elif isinstance(info, numpy.ndarray):
             ty = Tensor(info.dtype, info.shape)
             result[name] = Argument(
                 Argument.Attributes(
-                    name=attr_name, type=AttrType(ty), default=AttrTensor(info)
+                    name=attr_name,
+                    type=AttrType(value=ty, name="dummy"),
+                    default=AttrTensor(value=info, name="dummy"),
                 ),
                 BaseInputs(),
             ).outputs.arg
@@ -101,7 +107,7 @@ def initializer(arr: numpy.ndarray) -> Var:
         Var which is always equal to the respective value provided by `arr`.
     """
     return _Initializer(
-        _Initializer.Attributes(value=AttrTensor(arr)),
+        _Initializer.Attributes(value=AttrTensor(value=arr, name="dummy")),
         BaseInputs(),
     ).outputs.arg
 
diff --git a/src/spox/_node.py b/src/spox/_node.py
index 84a88b3e..9ab4af06 100644
--- a/src/spox/_node.py
+++ b/src/spox/_node.py
@@ -373,7 +373,7 @@ def to_onnx(
                     subgraph = build_subgraph(self, key, attr.value)
                     attr_proto = onnx.helper.make_attribute(key, subgraph)
                 else:
-                    attr_proto = attr._to_onnx(key)
+                    attr_proto = attr._to_onnx()
                 node_proto.attribute.append(attr_proto)
 
         return [node_proto]
diff --git a/src/spox/_public.py b/src/spox/_public.py
index d203b62a..fa2a0df4 100644
--- a/src/spox/_public.py
+++ b/src/spox/_public.py
@@ -33,7 +33,7 @@ def argument(typ: Type) -> Var:
         a model input to build a graph.
     """
     return _internal_op.Argument(
-        _internal_op.Argument.Attributes(type=AttrType(typ), default=None)
+        _internal_op.Argument.Attributes(type=AttrType(typ, "dummy"), default=None)
     ).outputs.arg
 
 
diff --git a/src/spox/_standard.py b/src/spox/_standard.py
index f765173c..c9c575d2 100644
--- a/src/spox/_standard.py
+++ b/src/spox/_standard.py
@@ -68,7 +68,7 @@ def to_singleton_onnx_model(
         try:
             current_fields = self_attrs.get_fields().items()
             non_cachable_attributes_recreated = {
-                k: type(v)(v.value) if v is not None else v
+                k: type(v)(v.value, k) if v is not None else v
                 for k, v in current_fields
                 if v is not None
                 and (
diff --git a/src/spox/opset/ai/onnx/ml/v3.py b/src/spox/opset/ai/onnx/ml/v3.py
index 25f46d27..842bedc9 100644
--- a/src/spox/opset/ai/onnx/ml/v3.py
+++ b/src/spox/opset/ai/onnx/ml/v3.py
@@ -719,7 +719,7 @@ def binarizer(
     """
     return _Binarizer(
         _Binarizer.Attributes(
-            threshold=AttrFloat32(threshold),
+            threshold=AttrFloat32(threshold, name="threshold"),
         ),
         _Binarizer.Inputs(
             X=X,
@@ -776,9 +776,9 @@ def cast_map(
     """
     return _CastMap(
         _CastMap.Attributes(
-            cast_to=AttrString(cast_to),
-            map_form=AttrString(map_form),
-            max_map=AttrInt64(max_map),
+            cast_to=AttrString(cast_to, name="cast_to"),
+            map_form=AttrString(map_form, name="map_form"),
+            max_map=AttrInt64(max_map, name="max_map"),
         ),
         _CastMap.Inputs(
             X=X,
@@ -843,10 +843,10 @@ def category_mapper(
     """
     return _CategoryMapper(
         _CategoryMapper.Attributes(
-            cats_int64s=AttrInt64s.maybe(cats_int64s),
-            cats_strings=AttrStrings.maybe(cats_strings),
-            default_int64=AttrInt64(default_int64),
-            default_string=AttrString(default_string),
+            cats_int64s=AttrInt64s.maybe(cats_int64s, name="cats_int64s"),
+            cats_strings=AttrStrings.maybe(cats_strings, name="cats_strings"),
+            default_int64=AttrInt64(default_int64, name="default_int64"),
+            default_string=AttrString(default_string, name="default_string"),
         ),
         _CategoryMapper.Inputs(
             X=X,
@@ -906,8 +906,12 @@ def dict_vectorizer(
     """
     return _DictVectorizer(
         _DictVectorizer.Attributes(
-            int64_vocabulary=AttrInt64s.maybe(int64_vocabulary),
-            string_vocabulary=AttrStrings.maybe(string_vocabulary),
+            int64_vocabulary=AttrInt64s.maybe(
+                int64_vocabulary, name="int64_vocabulary"
+            ),
+            string_vocabulary=AttrStrings.maybe(
+                string_vocabulary, name="string_vocabulary"
+            ),
         ),
         _DictVectorizer.Inputs(
             X=X,
@@ -951,7 +955,7 @@ def feature_vectorizer(
     """
     return _FeatureVectorizer(
         _FeatureVectorizer.Attributes(
-            inputdimensions=AttrInt64s.maybe(inputdimensions),
+            inputdimensions=AttrInt64s.maybe(inputdimensions, name="inputdimensions"),
         ),
         _FeatureVectorizer.Inputs(
             X=X,
@@ -1016,10 +1020,18 @@ def imputer(
     """
     return _Imputer(
         _Imputer.Attributes(
-            imputed_value_floats=AttrFloat32s.maybe(imputed_value_floats),
-            imputed_value_int64s=AttrInt64s.maybe(imputed_value_int64s),
-            replaced_value_float=AttrFloat32(replaced_value_float),
-            replaced_value_int64=AttrInt64(replaced_value_int64),
+            imputed_value_floats=AttrFloat32s.maybe(
+                imputed_value_floats, name="imputed_value_floats"
+            ),
+            imputed_value_int64s=AttrInt64s.maybe(
+                imputed_value_int64s, name="imputed_value_int64s"
+            ),
+            replaced_value_float=AttrFloat32(
+                replaced_value_float, name="replaced_value_float"
+            ),
+            replaced_value_int64=AttrInt64(
+                replaced_value_int64, name="replaced_value_int64"
+            ),
         ),
         _Imputer.Inputs(
             X=X,
@@ -1109,15 +1121,15 @@ def label_encoder(
     """
     return _LabelEncoder(
         _LabelEncoder.Attributes(
-            default_float=AttrFloat32(default_float),
-            default_int64=AttrInt64(default_int64),
-            default_string=AttrString(default_string),
-            keys_floats=AttrFloat32s.maybe(keys_floats),
-            keys_int64s=AttrInt64s.maybe(keys_int64s),
-            keys_strings=AttrStrings.maybe(keys_strings),
-            values_floats=AttrFloat32s.maybe(values_floats),
-            values_int64s=AttrInt64s.maybe(values_int64s),
-            values_strings=AttrStrings.maybe(values_strings),
+            default_float=AttrFloat32(default_float, name="default_float"),
+            default_int64=AttrInt64(default_int64, name="default_int64"),
+            default_string=AttrString(default_string, name="default_string"),
+            keys_floats=AttrFloat32s.maybe(keys_floats, name="keys_floats"),
+            keys_int64s=AttrInt64s.maybe(keys_int64s, name="keys_int64s"),
+            keys_strings=AttrStrings.maybe(keys_strings, name="keys_strings"),
+            values_floats=AttrFloat32s.maybe(values_floats, name="values_floats"),
+            values_int64s=AttrInt64s.maybe(values_int64s, name="values_int64s"),
+            values_strings=AttrStrings.maybe(values_strings, name="values_strings"),
         ),
         _LabelEncoder.Inputs(
             X=X,
@@ -1184,12 +1196,16 @@ def linear_classifier(
     """
     return _LinearClassifier(
         _LinearClassifier.Attributes(
-            classlabels_ints=AttrInt64s.maybe(classlabels_ints),
-            classlabels_strings=AttrStrings.maybe(classlabels_strings),
-            coefficients=AttrFloat32s(coefficients),
-            intercepts=AttrFloat32s.maybe(intercepts),
-            multi_class=AttrInt64(multi_class),
-            post_transform=AttrString(post_transform),
+            classlabels_ints=AttrInt64s.maybe(
+                classlabels_ints, name="classlabels_ints"
+            ),
+            classlabels_strings=AttrStrings.maybe(
+                classlabels_strings, name="classlabels_strings"
+            ),
+            coefficients=AttrFloat32s(coefficients, name="coefficients"),
+            intercepts=AttrFloat32s.maybe(intercepts, name="intercepts"),
+            multi_class=AttrInt64(multi_class, name="multi_class"),
+            post_transform=AttrString(post_transform, name="post_transform"),
         ),
         _LinearClassifier.Inputs(
             X=X,
@@ -1248,10 +1264,10 @@ def linear_regressor(
     """
     return _LinearRegressor(
         _LinearRegressor.Attributes(
-            coefficients=AttrFloat32s.maybe(coefficients),
-            intercepts=AttrFloat32s.maybe(intercepts),
-            post_transform=AttrString(post_transform),
-            targets=AttrInt64(targets),
+            coefficients=AttrFloat32s.maybe(coefficients, name="coefficients"),
+            intercepts=AttrFloat32s.maybe(intercepts, name="intercepts"),
+            post_transform=AttrString(post_transform, name="post_transform"),
+            targets=AttrInt64(targets, name="targets"),
         ),
         _LinearRegressor.Inputs(
             X=X,
@@ -1297,7 +1313,7 @@ def normalizer(
     """
     return _Normalizer(
         _Normalizer.Attributes(
-            norm=AttrString(norm),
+            norm=AttrString(norm, name="norm"),
         ),
         _Normalizer.Inputs(
             X=X,
@@ -1356,9 +1372,9 @@ def one_hot_encoder(
     """
     return _OneHotEncoder(
         _OneHotEncoder.Attributes(
-            cats_int64s=AttrInt64s.maybe(cats_int64s),
-            cats_strings=AttrStrings.maybe(cats_strings),
-            zeros=AttrInt64(zeros),
+            cats_int64s=AttrInt64s.maybe(cats_int64s, name="cats_int64s"),
+            cats_strings=AttrStrings.maybe(cats_strings, name="cats_strings"),
+            zeros=AttrInt64(zeros, name="zeros"),
         ),
         _OneHotEncoder.Inputs(
             X=X,
@@ -1450,17 +1466,23 @@ def svmclassifier(
     """
     return _SVMClassifier(
         _SVMClassifier.Attributes(
-            classlabels_ints=AttrInt64s.maybe(classlabels_ints),
-            classlabels_strings=AttrStrings.maybe(classlabels_strings),
-            coefficients=AttrFloat32s.maybe(coefficients),
-            kernel_params=AttrFloat32s.maybe(kernel_params),
-            kernel_type=AttrString(kernel_type),
-            post_transform=AttrString(post_transform),
-            prob_a=AttrFloat32s.maybe(prob_a),
-            prob_b=AttrFloat32s.maybe(prob_b),
-            rho=AttrFloat32s.maybe(rho),
-            support_vectors=AttrFloat32s.maybe(support_vectors),
-            vectors_per_class=AttrInt64s.maybe(vectors_per_class),
+            classlabels_ints=AttrInt64s.maybe(
+                classlabels_ints, name="classlabels_ints"
+            ),
+            classlabels_strings=AttrStrings.maybe(
+                classlabels_strings, name="classlabels_strings"
+            ),
+            coefficients=AttrFloat32s.maybe(coefficients, name="coefficients"),
+            kernel_params=AttrFloat32s.maybe(kernel_params, name="kernel_params"),
+            kernel_type=AttrString(kernel_type, name="kernel_type"),
+            post_transform=AttrString(post_transform, name="post_transform"),
+            prob_a=AttrFloat32s.maybe(prob_a, name="prob_a"),
+            prob_b=AttrFloat32s.maybe(prob_b, name="prob_b"),
+            rho=AttrFloat32s.maybe(rho, name="rho"),
+            support_vectors=AttrFloat32s.maybe(support_vectors, name="support_vectors"),
+            vectors_per_class=AttrInt64s.maybe(
+                vectors_per_class, name="vectors_per_class"
+            ),
         ),
         _SVMClassifier.Inputs(
             X=X,
@@ -1531,14 +1553,14 @@ def svmregressor(
     """
     return _SVMRegressor(
         _SVMRegressor.Attributes(
-            coefficients=AttrFloat32s.maybe(coefficients),
-            kernel_params=AttrFloat32s.maybe(kernel_params),
-            kernel_type=AttrString(kernel_type),
-            n_supports=AttrInt64(n_supports),
-            one_class=AttrInt64(one_class),
-            post_transform=AttrString(post_transform),
-            rho=AttrFloat32s.maybe(rho),
-            support_vectors=AttrFloat32s.maybe(support_vectors),
+            coefficients=AttrFloat32s.maybe(coefficients, name="coefficients"),
+            kernel_params=AttrFloat32s.maybe(kernel_params, name="kernel_params"),
+            kernel_type=AttrString(kernel_type, name="kernel_type"),
+            n_supports=AttrInt64(n_supports, name="n_supports"),
+            one_class=AttrInt64(one_class, name="one_class"),
+            post_transform=AttrString(post_transform, name="post_transform"),
+            rho=AttrFloat32s.maybe(rho, name="rho"),
+            support_vectors=AttrFloat32s.maybe(support_vectors, name="support_vectors"),
         ),
         _SVMRegressor.Inputs(
             X=X,
@@ -1587,8 +1609,8 @@ def scaler(
     """
     return _Scaler(
         _Scaler.Attributes(
-            offset=AttrFloat32s.maybe(offset),
-            scale=AttrFloat32s.maybe(scale),
+            offset=AttrFloat32s.maybe(offset, name="offset"),
+            scale=AttrFloat32s.maybe(scale, name="scale"),
         ),
         _Scaler.Inputs(
             X=X,
@@ -1732,29 +1754,47 @@ def tree_ensemble_classifier(
     """
     return _TreeEnsembleClassifier(
         _TreeEnsembleClassifier.Attributes(
-            base_values=AttrFloat32s.maybe(base_values),
-            base_values_as_tensor=AttrTensor.maybe(base_values_as_tensor),
-            class_ids=AttrInt64s.maybe(class_ids),
-            class_nodeids=AttrInt64s.maybe(class_nodeids),
-            class_treeids=AttrInt64s.maybe(class_treeids),
-            class_weights=AttrFloat32s.maybe(class_weights),
-            class_weights_as_tensor=AttrTensor.maybe(class_weights_as_tensor),
-            classlabels_int64s=AttrInt64s.maybe(classlabels_int64s),
-            classlabels_strings=AttrStrings.maybe(classlabels_strings),
-            nodes_falsenodeids=AttrInt64s.maybe(nodes_falsenodeids),
-            nodes_featureids=AttrInt64s.maybe(nodes_featureids),
-            nodes_hitrates=AttrFloat32s.maybe(nodes_hitrates),
-            nodes_hitrates_as_tensor=AttrTensor.maybe(nodes_hitrates_as_tensor),
+            base_values=AttrFloat32s.maybe(base_values, name="base_values"),
+            base_values_as_tensor=AttrTensor.maybe(
+                base_values_as_tensor, name="base_values_as_tensor"
+            ),
+            class_ids=AttrInt64s.maybe(class_ids, name="class_ids"),
+            class_nodeids=AttrInt64s.maybe(class_nodeids, name="class_nodeids"),
+            class_treeids=AttrInt64s.maybe(class_treeids, name="class_treeids"),
+            class_weights=AttrFloat32s.maybe(class_weights, name="class_weights"),
+            class_weights_as_tensor=AttrTensor.maybe(
+                class_weights_as_tensor, name="class_weights_as_tensor"
+            ),
+            classlabels_int64s=AttrInt64s.maybe(
+                classlabels_int64s, name="classlabels_int64s"
+            ),
+            classlabels_strings=AttrStrings.maybe(
+                classlabels_strings, name="classlabels_strings"
+            ),
+            nodes_falsenodeids=AttrInt64s.maybe(
+                nodes_falsenodeids, name="nodes_falsenodeids"
+            ),
+            nodes_featureids=AttrInt64s.maybe(
+                nodes_featureids, name="nodes_featureids"
+            ),
+            nodes_hitrates=AttrFloat32s.maybe(nodes_hitrates, name="nodes_hitrates"),
+            nodes_hitrates_as_tensor=AttrTensor.maybe(
+                nodes_hitrates_as_tensor, name="nodes_hitrates_as_tensor"
+            ),
             nodes_missing_value_tracks_true=AttrInt64s.maybe(
-                nodes_missing_value_tracks_true
+                nodes_missing_value_tracks_true, name="nodes_missing_value_tracks_true"
+            ),
+            nodes_modes=AttrStrings.maybe(nodes_modes, name="nodes_modes"),
+            nodes_nodeids=AttrInt64s.maybe(nodes_nodeids, name="nodes_nodeids"),
+            nodes_treeids=AttrInt64s.maybe(nodes_treeids, name="nodes_treeids"),
+            nodes_truenodeids=AttrInt64s.maybe(
+                nodes_truenodeids, name="nodes_truenodeids"
             ),
-            nodes_modes=AttrStrings.maybe(nodes_modes),
-            nodes_nodeids=AttrInt64s.maybe(nodes_nodeids),
-            nodes_treeids=AttrInt64s.maybe(nodes_treeids),
-            nodes_truenodeids=AttrInt64s.maybe(nodes_truenodeids),
-            nodes_values=AttrFloat32s.maybe(nodes_values),
-            nodes_values_as_tensor=AttrTensor.maybe(nodes_values_as_tensor),
-            post_transform=AttrString(post_transform),
+            nodes_values=AttrFloat32s.maybe(nodes_values, name="nodes_values"),
+            nodes_values_as_tensor=AttrTensor.maybe(
+                nodes_values_as_tensor, name="nodes_values_as_tensor"
+            ),
+            post_transform=AttrString(post_transform, name="post_transform"),
         ),
         _TreeEnsembleClassifier.Inputs(
             X=X,
@@ -1894,29 +1934,45 @@ def tree_ensemble_regressor(
     """
     return _TreeEnsembleRegressor(
         _TreeEnsembleRegressor.Attributes(
-            aggregate_function=AttrString(aggregate_function),
-            base_values=AttrFloat32s.maybe(base_values),
-            base_values_as_tensor=AttrTensor.maybe(base_values_as_tensor),
-            n_targets=AttrInt64.maybe(n_targets),
-            nodes_falsenodeids=AttrInt64s.maybe(nodes_falsenodeids),
-            nodes_featureids=AttrInt64s.maybe(nodes_featureids),
-            nodes_hitrates=AttrFloat32s.maybe(nodes_hitrates),
-            nodes_hitrates_as_tensor=AttrTensor.maybe(nodes_hitrates_as_tensor),
+            aggregate_function=AttrString(
+                aggregate_function, name="aggregate_function"
+            ),
+            base_values=AttrFloat32s.maybe(base_values, name="base_values"),
+            base_values_as_tensor=AttrTensor.maybe(
+                base_values_as_tensor, name="base_values_as_tensor"
+            ),
+            n_targets=AttrInt64.maybe(n_targets, name="n_targets"),
+            nodes_falsenodeids=AttrInt64s.maybe(
+                nodes_falsenodeids, name="nodes_falsenodeids"
+            ),
+            nodes_featureids=AttrInt64s.maybe(
+                nodes_featureids, name="nodes_featureids"
+            ),
+            nodes_hitrates=AttrFloat32s.maybe(nodes_hitrates, name="nodes_hitrates"),
+            nodes_hitrates_as_tensor=AttrTensor.maybe(
+                nodes_hitrates_as_tensor, name="nodes_hitrates_as_tensor"
+            ),
             nodes_missing_value_tracks_true=AttrInt64s.maybe(
-                nodes_missing_value_tracks_true
+                nodes_missing_value_tracks_true, name="nodes_missing_value_tracks_true"
+            ),
+            nodes_modes=AttrStrings.maybe(nodes_modes, name="nodes_modes"),
+            nodes_nodeids=AttrInt64s.maybe(nodes_nodeids, name="nodes_nodeids"),
+            nodes_treeids=AttrInt64s.maybe(nodes_treeids, name="nodes_treeids"),
+            nodes_truenodeids=AttrInt64s.maybe(
+                nodes_truenodeids, name="nodes_truenodeids"
+            ),
+            nodes_values=AttrFloat32s.maybe(nodes_values, name="nodes_values"),
+            nodes_values_as_tensor=AttrTensor.maybe(
+                nodes_values_as_tensor, name="nodes_values_as_tensor"
+            ),
+            post_transform=AttrString(post_transform, name="post_transform"),
+            target_ids=AttrInt64s.maybe(target_ids, name="target_ids"),
+            target_nodeids=AttrInt64s.maybe(target_nodeids, name="target_nodeids"),
+            target_treeids=AttrInt64s.maybe(target_treeids, name="target_treeids"),
+            target_weights=AttrFloat32s.maybe(target_weights, name="target_weights"),
+            target_weights_as_tensor=AttrTensor.maybe(
+                target_weights_as_tensor, name="target_weights_as_tensor"
             ),
-            nodes_modes=AttrStrings.maybe(nodes_modes),
-            nodes_nodeids=AttrInt64s.maybe(nodes_nodeids),
-            nodes_treeids=AttrInt64s.maybe(nodes_treeids),
-            nodes_truenodeids=AttrInt64s.maybe(nodes_truenodeids),
-            nodes_values=AttrFloat32s.maybe(nodes_values),
-            nodes_values_as_tensor=AttrTensor.maybe(nodes_values_as_tensor),
-            post_transform=AttrString(post_transform),
-            target_ids=AttrInt64s.maybe(target_ids),
-            target_nodeids=AttrInt64s.maybe(target_nodeids),
-            target_treeids=AttrInt64s.maybe(target_treeids),
-            target_weights=AttrFloat32s.maybe(target_weights),
-            target_weights_as_tensor=AttrTensor.maybe(target_weights_as_tensor),
         ),
         _TreeEnsembleRegressor.Inputs(
             X=X,
@@ -1966,8 +2022,12 @@ def zip_map(
     """
     return _ZipMap(
         _ZipMap.Attributes(
-            classlabels_int64s=AttrInt64s.maybe(classlabels_int64s),
-            classlabels_strings=AttrStrings.maybe(classlabels_strings),
+            classlabels_int64s=AttrInt64s.maybe(
+                classlabels_int64s, name="classlabels_int64s"
+            ),
+            classlabels_strings=AttrStrings.maybe(
+                classlabels_strings, name="classlabels_strings"
+            ),
         ),
         _ZipMap.Inputs(
             X=X,
diff --git a/src/spox/opset/ai/onnx/ml/v4.py b/src/spox/opset/ai/onnx/ml/v4.py
index 815c4251..fecdcf0d 100644
--- a/src/spox/opset/ai/onnx/ml/v4.py
+++ b/src/spox/opset/ai/onnx/ml/v4.py
@@ -208,18 +208,18 @@ def label_encoder(
     """
     return _LabelEncoder(
         _LabelEncoder.Attributes(
-            default_float=AttrFloat32(default_float),
-            default_int64=AttrInt64(default_int64),
-            default_string=AttrString(default_string),
-            default_tensor=AttrTensor(default_tensor),
-            keys_floats=AttrFloat32s.maybe(keys_floats),
-            keys_int64s=AttrInt64s.maybe(keys_int64s),
-            keys_strings=AttrStrings.maybe(keys_strings),
-            keys_tensor=AttrTensor.maybe(keys_tensor),
-            values_floats=AttrFloat32s.maybe(values_floats),
-            values_int64s=AttrInt64s.maybe(values_int64s),
-            values_strings=AttrStrings.maybe(values_strings),
-            values_tensor=AttrTensor.maybe(values_tensor),
+            default_float=AttrFloat32(default_float, name="default_float"),
+            default_int64=AttrInt64(default_int64, name="default_int64"),
+            default_string=AttrString(default_string, name="default_string"),
+            default_tensor=AttrTensor(default_tensor, name="default_tensor"),
+            keys_floats=AttrFloat32s.maybe(keys_floats, name="keys_floats"),
+            keys_int64s=AttrInt64s.maybe(keys_int64s, name="keys_int64s"),
+            keys_strings=AttrStrings.maybe(keys_strings, name="keys_strings"),
+            keys_tensor=AttrTensor.maybe(keys_tensor, name="keys_tensor"),
+            values_floats=AttrFloat32s.maybe(values_floats, name="values_floats"),
+            values_int64s=AttrInt64s.maybe(values_int64s, name="values_int64s"),
+            values_strings=AttrStrings.maybe(values_strings, name="values_strings"),
+            values_tensor=AttrTensor.maybe(values_tensor, name="values_tensor"),
         ),
         _LabelEncoder.Inputs(
             X=X,
diff --git a/src/spox/opset/ai/onnx/v17.py b/src/spox/opset/ai/onnx/v17.py
index c7030fba..abe8a79c 100644
--- a/src/spox/opset/ai/onnx/v17.py
+++ b/src/spox/opset/ai/onnx/v17.py
@@ -4182,9 +4182,9 @@ def arg_max(
     """
     return _ArgMax(
         _ArgMax.Attributes(
-            axis=AttrInt64(axis),
-            keepdims=AttrInt64(keepdims),
-            select_last_index=AttrInt64(select_last_index),
+            axis=AttrInt64(axis, name="axis"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            select_last_index=AttrInt64(select_last_index, name="select_last_index"),
         ),
         _ArgMax.Inputs(
             data=data,
@@ -4242,9 +4242,9 @@ def arg_min(
     """
     return _ArgMin(
         _ArgMin.Attributes(
-            axis=AttrInt64(axis),
-            keepdims=AttrInt64(keepdims),
-            select_last_index=AttrInt64(select_last_index),
+            axis=AttrInt64(axis, name="axis"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            select_last_index=AttrInt64(select_last_index, name="select_last_index"),
         ),
         _ArgMin.Inputs(
             data=data,
@@ -4512,12 +4512,12 @@ def average_pool(
     """
     return _AveragePool(
         _AveragePool.Attributes(
-            auto_pad=AttrString(auto_pad),
-            ceil_mode=AttrInt64(ceil_mode),
-            count_include_pad=AttrInt64(count_include_pad),
-            kernel_shape=AttrInt64s(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            ceil_mode=AttrInt64(ceil_mode, name="ceil_mode"),
+            count_include_pad=AttrInt64(count_include_pad, name="count_include_pad"),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _AveragePool.Inputs(
             X=X,
@@ -4647,9 +4647,9 @@ def batch_normalization(
     """
     return _BatchNormalization(
         _BatchNormalization.Attributes(
-            epsilon=AttrFloat32(epsilon),
-            momentum=AttrFloat32(momentum),
-            training_mode=AttrInt64(training_mode),
+            epsilon=AttrFloat32(epsilon, name="epsilon"),
+            momentum=AttrFloat32(momentum, name="momentum"),
+            training_mode=AttrInt64(training_mode, name="training_mode"),
         ),
         _BatchNormalization.Inputs(
             X=X,
@@ -4708,8 +4708,8 @@ def bernoulli(
     """
     return _Bernoulli(
         _Bernoulli.Attributes(
-            dtype=AttrDtype.maybe(dtype),
-            seed=AttrFloat32.maybe(seed),
+            dtype=AttrDtype.maybe(dtype, name="dtype"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
         ),
         _Bernoulli.Inputs(
             input=input,
@@ -4769,7 +4769,7 @@ def bit_shift(
     """
     return _BitShift(
         _BitShift.Attributes(
-            direction=AttrString(direction),
+            direction=AttrString(direction, name="direction"),
         ),
         _BitShift.Inputs(
             X=X,
@@ -4821,8 +4821,8 @@ def blackman_window(
     """
     return _BlackmanWindow(
         _BlackmanWindow.Attributes(
-            output_datatype=AttrInt64(output_datatype),
-            periodic=AttrInt64(periodic),
+            output_datatype=AttrInt64(output_datatype, name="output_datatype"),
+            periodic=AttrInt64(periodic, name="periodic"),
         ),
         _BlackmanWindow.Inputs(
             size=size,
@@ -4914,7 +4914,7 @@ def cast(
     """
     return _Cast(
         _Cast.Attributes(
-            to=AttrDtype(to),
+            to=AttrDtype(to, name="to"),
         ),
         _Cast.Inputs(
             input=input,
@@ -5039,7 +5039,7 @@ def celu(
     """
     return _Celu(
         _Celu.Attributes(
-            alpha=AttrFloat32(alpha),
+            alpha=AttrFloat32(alpha, name="alpha"),
         ),
         _Celu.Inputs(
             X=X,
@@ -5142,7 +5142,7 @@ def compress(
     """
     return _Compress(
         _Compress.Attributes(
-            axis=AttrInt64.maybe(axis),
+            axis=AttrInt64.maybe(axis, name="axis"),
         ),
         _Compress.Inputs(
             input=input,
@@ -5186,7 +5186,7 @@ def concat(
     """
     return _Concat(
         _Concat.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Concat.Inputs(
             inputs=inputs,
@@ -5238,8 +5238,8 @@ def concat_from_sequence(
     """
     return _ConcatFromSequence(
         _ConcatFromSequence.Attributes(
-            axis=AttrInt64(axis),
-            new_axis=AttrInt64(new_axis),
+            axis=AttrInt64(axis, name="axis"),
+            new_axis=AttrInt64(new_axis, name="new_axis"),
         ),
         _ConcatFromSequence.Inputs(
             input_sequence=input_sequence,
@@ -5304,13 +5304,13 @@ def constant(
     """
     return _Constant(
         _Constant.Attributes(
-            value=AttrTensor.maybe(value),
-            value_float=AttrFloat32.maybe(value_float),
-            value_floats=AttrFloat32s.maybe(value_floats),
-            value_int=AttrInt64.maybe(value_int),
-            value_ints=AttrInt64s.maybe(value_ints),
-            value_string=AttrString.maybe(value_string),
-            value_strings=AttrStrings.maybe(value_strings),
+            value=AttrTensor.maybe(value, name="value"),
+            value_float=AttrFloat32.maybe(value_float, name="value_float"),
+            value_floats=AttrFloat32s.maybe(value_floats, name="value_floats"),
+            value_int=AttrInt64.maybe(value_int, name="value_int"),
+            value_ints=AttrInt64s.maybe(value_ints, name="value_ints"),
+            value_string=AttrString.maybe(value_string, name="value_string"),
+            value_strings=AttrStrings.maybe(value_strings, name="value_strings"),
         ),
         _Constant.Inputs(),
     ).outputs.output
@@ -5355,7 +5355,7 @@ def constant_of_shape(
     """
     return _ConstantOfShape(
         _ConstantOfShape.Attributes(
-            value=AttrTensor.maybe(value),
+            value=AttrTensor.maybe(value, name="value"),
         ),
         _ConstantOfShape.Inputs(
             input=input,
@@ -5462,12 +5462,12 @@ def conv(
     """
     return _Conv(
         _Conv.Attributes(
-            auto_pad=AttrString(auto_pad),
-            dilations=AttrInt64s.maybe(dilations),
-            group=AttrInt64(group),
-            kernel_shape=AttrInt64s.maybe(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            group=AttrInt64(group, name="group"),
+            kernel_shape=AttrInt64s.maybe(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _Conv.Inputs(
             X=X,
@@ -5587,12 +5587,12 @@ def conv_integer(
     """
     return _ConvInteger(
         _ConvInteger.Attributes(
-            auto_pad=AttrString(auto_pad),
-            dilations=AttrInt64s.maybe(dilations),
-            group=AttrInt64(group),
-            kernel_shape=AttrInt64s.maybe(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            group=AttrInt64(group, name="group"),
+            kernel_shape=AttrInt64s.maybe(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _ConvInteger.Inputs(
             x=x,
@@ -5733,14 +5733,14 @@ def conv_transpose(
     """
     return _ConvTranspose(
         _ConvTranspose.Attributes(
-            auto_pad=AttrString(auto_pad),
-            dilations=AttrInt64s.maybe(dilations),
-            group=AttrInt64(group),
-            kernel_shape=AttrInt64s.maybe(kernel_shape),
-            output_padding=AttrInt64s.maybe(output_padding),
-            output_shape=AttrInt64s.maybe(output_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            group=AttrInt64(group, name="group"),
+            kernel_shape=AttrInt64s.maybe(kernel_shape, name="kernel_shape"),
+            output_padding=AttrInt64s.maybe(output_padding, name="output_padding"),
+            output_shape=AttrInt64s.maybe(output_shape, name="output_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _ConvTranspose.Inputs(
             X=X,
@@ -5883,8 +5883,8 @@ def cumsum(
     """
     return _CumSum(
         _CumSum.Attributes(
-            exclusive=AttrInt64(exclusive),
-            reverse=AttrInt64(reverse),
+            exclusive=AttrInt64(exclusive, name="exclusive"),
+            reverse=AttrInt64(reverse, name="reverse"),
         ),
         _CumSum.Inputs(
             x=x,
@@ -5968,9 +5968,9 @@ def dft(
     """
     return _DFT(
         _DFT.Attributes(
-            axis=AttrInt64(axis),
-            inverse=AttrInt64(inverse),
-            onesided=AttrInt64(onesided),
+            axis=AttrInt64(axis, name="axis"),
+            inverse=AttrInt64(inverse, name="inverse"),
+            onesided=AttrInt64(onesided, name="onesided"),
         ),
         _DFT.Inputs(
             input=input,
@@ -6043,8 +6043,8 @@ def depth_to_space(
     """
     return _DepthToSpace(
         _DepthToSpace.Attributes(
-            blocksize=AttrInt64(blocksize),
-            mode=AttrString(mode),
+            blocksize=AttrInt64(blocksize, name="blocksize"),
+            mode=AttrString(mode, name="mode"),
         ),
         _DepthToSpace.Inputs(
             input=input,
@@ -6104,7 +6104,7 @@ def dequantize_linear(
     """
     return _DequantizeLinear(
         _DequantizeLinear.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _DequantizeLinear.Inputs(
             x=x,
@@ -6276,7 +6276,7 @@ def dropout(
     """
     return _Dropout(
         _Dropout.Attributes(
-            seed=AttrInt64.maybe(seed),
+            seed=AttrInt64.maybe(seed, name="seed"),
         ),
         _Dropout.Inputs(
             data=data,
@@ -6425,7 +6425,7 @@ def einsum(
     """
     return _Einsum(
         _Einsum.Attributes(
-            equation=AttrString(equation),
+            equation=AttrString(equation, name="equation"),
         ),
         _Einsum.Inputs(
             Inputs=Inputs,
@@ -6468,7 +6468,7 @@ def elu(
     """
     return _Elu(
         _Elu.Attributes(
-            alpha=AttrFloat32(alpha),
+            alpha=AttrFloat32(alpha, name="alpha"),
         ),
         _Elu.Inputs(
             X=X,
@@ -6685,8 +6685,8 @@ def eye_like(
     """
     return _EyeLike(
         _EyeLike.Attributes(
-            dtype=AttrDtype.maybe(dtype),
-            k=AttrInt64(k),
+            dtype=AttrDtype.maybe(dtype, name="dtype"),
+            k=AttrInt64(k, name="k"),
         ),
         _EyeLike.Inputs(
             input=input,
@@ -6735,7 +6735,7 @@ def flatten(
     """
     return _Flatten(
         _Flatten.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Flatten.Inputs(
             input=input,
@@ -6953,14 +6953,18 @@ def gru(
     """
     return _GRU(
         _GRU.Attributes(
-            activation_alpha=AttrFloat32s.maybe(activation_alpha),
-            activation_beta=AttrFloat32s.maybe(activation_beta),
-            activations=AttrStrings.maybe(activations),
-            clip=AttrFloat32.maybe(clip),
-            direction=AttrString(direction),
-            hidden_size=AttrInt64.maybe(hidden_size),
-            layout=AttrInt64(layout),
-            linear_before_reset=AttrInt64(linear_before_reset),
+            activation_alpha=AttrFloat32s.maybe(
+                activation_alpha, name="activation_alpha"
+            ),
+            activation_beta=AttrFloat32s.maybe(activation_beta, name="activation_beta"),
+            activations=AttrStrings.maybe(activations, name="activations"),
+            clip=AttrFloat32.maybe(clip, name="clip"),
+            direction=AttrString(direction, name="direction"),
+            hidden_size=AttrInt64.maybe(hidden_size, name="hidden_size"),
+            layout=AttrInt64(layout, name="layout"),
+            linear_before_reset=AttrInt64(
+                linear_before_reset, name="linear_before_reset"
+            ),
         ),
         _GRU.Inputs(
             X=X,
@@ -7061,7 +7065,7 @@ def gather(
     """
     return _Gather(
         _Gather.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Gather.Inputs(
             data=data,
@@ -7166,7 +7170,7 @@ def gather_elements(
     """
     return _GatherElements(
         _GatherElements.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _GatherElements.Inputs(
             data=data,
@@ -7316,7 +7320,7 @@ def gather_nd(
     """
     return _GatherND(
         _GatherND.Attributes(
-            batch_dims=AttrInt64(batch_dims),
+            batch_dims=AttrInt64(batch_dims, name="batch_dims"),
         ),
         _GatherND.Inputs(
             data=data,
@@ -7400,10 +7404,10 @@ def gemm(
     """
     return _Gemm(
         _Gemm.Attributes(
-            alpha=AttrFloat32(alpha),
-            beta=AttrFloat32(beta),
-            transA=AttrInt64(transA),
-            transB=AttrInt64(transB),
+            alpha=AttrFloat32(alpha, name="alpha"),
+            beta=AttrFloat32(beta, name="beta"),
+            transA=AttrInt64(transA, name="transA"),
+            transB=AttrInt64(transB, name="transB"),
         ),
         _Gemm.Inputs(
             A=A,
@@ -7496,7 +7500,7 @@ def global_lp_pool(
     """
     return _GlobalLpPool(
         _GlobalLpPool.Attributes(
-            p=AttrInt64(p),
+            p=AttrInt64(p, name="p"),
         ),
         _GlobalLpPool.Inputs(
             X=X,
@@ -7720,9 +7724,9 @@ def grid_sample(
     """
     return _GridSample(
         _GridSample.Attributes(
-            align_corners=AttrInt64(align_corners),
-            mode=AttrString(mode),
-            padding_mode=AttrString(padding_mode),
+            align_corners=AttrInt64(align_corners, name="align_corners"),
+            mode=AttrString(mode, name="mode"),
+            padding_mode=AttrString(padding_mode, name="padding_mode"),
         ),
         _GridSample.Inputs(
             X=X,
@@ -7774,8 +7778,8 @@ def hamming_window(
     """
     return _HammingWindow(
         _HammingWindow.Attributes(
-            output_datatype=AttrInt64(output_datatype),
-            periodic=AttrInt64(periodic),
+            output_datatype=AttrInt64(output_datatype, name="output_datatype"),
+            periodic=AttrInt64(periodic, name="periodic"),
         ),
         _HammingWindow.Inputs(
             size=size,
@@ -7826,8 +7830,8 @@ def hann_window(
     """
     return _HannWindow(
         _HannWindow.Attributes(
-            output_datatype=AttrInt64(output_datatype),
-            periodic=AttrInt64(periodic),
+            output_datatype=AttrInt64(output_datatype, name="output_datatype"),
+            periodic=AttrInt64(periodic, name="periodic"),
         ),
         _HannWindow.Inputs(
             size=size,
@@ -7873,8 +7877,8 @@ def hard_sigmoid(
     """
     return _HardSigmoid(
         _HardSigmoid.Attributes(
-            alpha=AttrFloat32(alpha),
-            beta=AttrFloat32(beta),
+            alpha=AttrFloat32(alpha, name="alpha"),
+            beta=AttrFloat32(beta, name="beta"),
         ),
         _HardSigmoid.Inputs(
             X=X,
@@ -7959,7 +7963,7 @@ def hardmax(
     """
     return _Hardmax(
         _Hardmax.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Hardmax.Inputs(
             input=input,
@@ -8056,8 +8060,8 @@ def if_(
     _then_branch_subgraph: Graph = subgraph((), then_branch)
     return _If(
         _If.Attributes(
-            else_branch=AttrGraph(_else_branch_subgraph),
-            then_branch=AttrGraph(_then_branch_subgraph),
+            else_branch=AttrGraph(_else_branch_subgraph, name="else_branch"),
+            then_branch=AttrGraph(_then_branch_subgraph, name="then_branch"),
         ),
         _If.Inputs(
             cond=cond,
@@ -8114,7 +8118,7 @@ def instance_normalization(
     """
     return _InstanceNormalization(
         _InstanceNormalization.Attributes(
-            epsilon=AttrFloat32(epsilon),
+            epsilon=AttrFloat32(epsilon, name="epsilon"),
         ),
         _InstanceNormalization.Inputs(
             input=input,
@@ -8165,8 +8169,8 @@ def isinf(
     """
     return _IsInf(
         _IsInf.Attributes(
-            detect_negative=AttrInt64(detect_negative),
-            detect_positive=AttrInt64(detect_positive),
+            detect_negative=AttrInt64(detect_negative, name="detect_negative"),
+            detect_positive=AttrInt64(detect_positive, name="detect_positive"),
         ),
         _IsInf.Inputs(
             X=X,
@@ -8269,10 +8273,10 @@ def lrn(
     """
     return _LRN(
         _LRN.Attributes(
-            alpha=AttrFloat32(alpha),
-            beta=AttrFloat32(beta),
-            bias=AttrFloat32(bias),
-            size=AttrInt64(size),
+            alpha=AttrFloat32(alpha, name="alpha"),
+            beta=AttrFloat32(beta, name="beta"),
+            bias=AttrFloat32(bias, name="bias"),
+            size=AttrInt64(size, name="size"),
         ),
         _LRN.Inputs(
             X=X,
@@ -8476,14 +8480,16 @@ def lstm(
     """
     return _LSTM(
         _LSTM.Attributes(
-            activation_alpha=AttrFloat32s.maybe(activation_alpha),
-            activation_beta=AttrFloat32s.maybe(activation_beta),
-            activations=AttrStrings.maybe(activations),
-            clip=AttrFloat32.maybe(clip),
-            direction=AttrString(direction),
-            hidden_size=AttrInt64.maybe(hidden_size),
-            input_forget=AttrInt64(input_forget),
-            layout=AttrInt64(layout),
+            activation_alpha=AttrFloat32s.maybe(
+                activation_alpha, name="activation_alpha"
+            ),
+            activation_beta=AttrFloat32s.maybe(activation_beta, name="activation_beta"),
+            activations=AttrStrings.maybe(activations, name="activations"),
+            clip=AttrFloat32.maybe(clip, name="clip"),
+            direction=AttrString(direction, name="direction"),
+            hidden_size=AttrInt64.maybe(hidden_size, name="hidden_size"),
+            input_forget=AttrInt64(input_forget, name="input_forget"),
+            layout=AttrInt64(layout, name="layout"),
         ),
         _LSTM.Inputs(
             X=X,
@@ -8579,9 +8585,9 @@ def layer_normalization(
     """
     return _LayerNormalization(
         _LayerNormalization.Attributes(
-            axis=AttrInt64(axis),
-            epsilon=AttrFloat32(epsilon),
-            stash_type=AttrInt64(stash_type),
+            axis=AttrInt64(axis, name="axis"),
+            epsilon=AttrFloat32(epsilon, name="epsilon"),
+            stash_type=AttrInt64(stash_type, name="stash_type"),
         ),
         _LayerNormalization.Inputs(
             X=X,
@@ -8626,7 +8632,7 @@ def leaky_relu(
     """
     return _LeakyRelu(
         _LeakyRelu.Attributes(
-            alpha=AttrFloat32(alpha),
+            alpha=AttrFloat32(alpha, name="alpha"),
         ),
         _LeakyRelu.Inputs(
             X=X,
@@ -8797,7 +8803,7 @@ def log_softmax(
     """
     return _LogSoftmax(
         _LogSoftmax.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _LogSoftmax.Inputs(
             input=input,
@@ -8988,7 +8994,7 @@ def loop(
     )
     return _Loop(
         _Loop.Attributes(
-            body=AttrGraph(_body_subgraph),
+            body=AttrGraph(_body_subgraph, name="body"),
         ),
         _Loop.Inputs(
             M=M,
@@ -9035,8 +9041,8 @@ def lp_normalization(
     """
     return _LpNormalization(
         _LpNormalization.Attributes(
-            axis=AttrInt64(axis),
-            p=AttrInt64(p),
+            axis=AttrInt64(axis, name="axis"),
+            p=AttrInt64(p, name="p"),
         ),
         _LpNormalization.Inputs(
             input=input,
@@ -9117,11 +9123,11 @@ def lp_pool(
     """
     return _LpPool(
         _LpPool.Attributes(
-            auto_pad=AttrString(auto_pad),
-            kernel_shape=AttrInt64s(kernel_shape),
-            p=AttrInt64(p),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            p=AttrInt64(p, name="p"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _LpPool.Inputs(
             X=X,
@@ -9406,13 +9412,13 @@ def max_pool(
     """
     return _MaxPool(
         _MaxPool.Attributes(
-            auto_pad=AttrString(auto_pad),
-            ceil_mode=AttrInt64(ceil_mode),
-            dilations=AttrInt64s.maybe(dilations),
-            kernel_shape=AttrInt64s(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            storage_order=AttrInt64(storage_order),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            ceil_mode=AttrInt64(ceil_mode, name="ceil_mode"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            storage_order=AttrInt64(storage_order, name="storage_order"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _MaxPool.Inputs(
             X=X,
@@ -9467,8 +9473,8 @@ def max_roi_pool(
     """
     return _MaxRoiPool(
         _MaxRoiPool.Attributes(
-            pooled_shape=AttrInt64s(pooled_shape),
-            spatial_scale=AttrFloat32(spatial_scale),
+            pooled_shape=AttrInt64s(pooled_shape, name="pooled_shape"),
+            spatial_scale=AttrFloat32(spatial_scale, name="spatial_scale"),
         ),
         _MaxRoiPool.Inputs(
             X=X,
@@ -9575,9 +9581,9 @@ def max_unpool(
     """
     return _MaxUnpool(
         _MaxUnpool.Attributes(
-            kernel_shape=AttrInt64s(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _MaxUnpool.Inputs(
             X=X,
@@ -9661,7 +9667,7 @@ def mean_variance_normalization(
     """
     return _MeanVarianceNormalization(
         _MeanVarianceNormalization.Attributes(
-            axes=AttrInt64s(axes),
+            axes=AttrInt64s(axes, name="axes"),
         ),
         _MeanVarianceNormalization.Inputs(
             X=X,
@@ -9745,7 +9751,7 @@ def mel_weight_matrix(
     """
     return _MelWeightMatrix(
         _MelWeightMatrix.Attributes(
-            output_datatype=AttrInt64(output_datatype),
+            output_datatype=AttrInt64(output_datatype, name="output_datatype"),
         ),
         _MelWeightMatrix.Inputs(
             num_mel_bins=num_mel_bins,
@@ -9848,7 +9854,7 @@ def mod(
     """
     return _Mod(
         _Mod.Attributes(
-            fmod=AttrInt64(fmod),
+            fmod=AttrInt64(fmod, name="fmod"),
         ),
         _Mod.Inputs(
             A=A,
@@ -9952,9 +9958,9 @@ def multinomial(
     """
     return _Multinomial(
         _Multinomial.Attributes(
-            dtype=AttrDtype(dtype),
-            sample_size=AttrInt64(sample_size),
-            seed=AttrFloat32.maybe(seed),
+            dtype=AttrDtype(dtype, name="dtype"),
+            sample_size=AttrInt64(sample_size, name="sample_size"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
         ),
         _Multinomial.Inputs(
             input=input,
@@ -10156,8 +10162,8 @@ def negative_log_likelihood_loss(
     """
     return _NegativeLogLikelihoodLoss(
         _NegativeLogLikelihoodLoss.Attributes(
-            ignore_index=AttrInt64.maybe(ignore_index),
-            reduction=AttrString(reduction),
+            ignore_index=AttrInt64.maybe(ignore_index, name="ignore_index"),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _NegativeLogLikelihoodLoss.Inputs(
             input=input,
@@ -10235,7 +10241,7 @@ def non_max_suppression(
     """
     return _NonMaxSuppression(
         _NonMaxSuppression.Attributes(
-            center_point_box=AttrInt64(center_point_box),
+            center_point_box=AttrInt64(center_point_box, name="center_point_box"),
         ),
         _NonMaxSuppression.Inputs(
             boxes=boxes,
@@ -10400,7 +10406,7 @@ def one_hot(
     """
     return _OneHot(
         _OneHot.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _OneHot.Inputs(
             indices=indices,
@@ -10445,7 +10451,7 @@ def optional(
     """
     return _Optional(
         _Optional.Attributes(
-            type=AttrType.maybe(type),
+            type=AttrType.maybe(type, name="type"),
         ),
         _Optional.Inputs(
             input=input,
@@ -10711,7 +10717,7 @@ def pad(
     """
     return _Pad(
         _Pad.Attributes(
-            mode=AttrString(mode),
+            mode=AttrString(mode, name="mode"),
         ),
         _Pad.Inputs(
             data=data,
@@ -10907,12 +10913,12 @@ def qlinear_conv(
     """
     return _QLinearConv(
         _QLinearConv.Attributes(
-            auto_pad=AttrString(auto_pad),
-            dilations=AttrInt64s.maybe(dilations),
-            group=AttrInt64(group),
-            kernel_shape=AttrInt64s.maybe(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            group=AttrInt64(group, name="group"),
+            kernel_shape=AttrInt64s.maybe(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _QLinearConv.Inputs(
             x=x,
@@ -11071,7 +11077,7 @@ def quantize_linear(
     """
     return _QuantizeLinear(
         _QuantizeLinear.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _QuantizeLinear.Inputs(
             x=x,
@@ -11235,13 +11241,15 @@ def rnn(
     """
     return _RNN(
         _RNN.Attributes(
-            activation_alpha=AttrFloat32s.maybe(activation_alpha),
-            activation_beta=AttrFloat32s.maybe(activation_beta),
-            activations=AttrStrings(activations),
-            clip=AttrFloat32.maybe(clip),
-            direction=AttrString(direction),
-            hidden_size=AttrInt64.maybe(hidden_size),
-            layout=AttrInt64(layout),
+            activation_alpha=AttrFloat32s.maybe(
+                activation_alpha, name="activation_alpha"
+            ),
+            activation_beta=AttrFloat32s.maybe(activation_beta, name="activation_beta"),
+            activations=AttrStrings(activations, name="activations"),
+            clip=AttrFloat32.maybe(clip, name="clip"),
+            direction=AttrString(direction, name="direction"),
+            hidden_size=AttrInt64.maybe(hidden_size, name="hidden_size"),
+            layout=AttrInt64(layout, name="layout"),
         ),
         _RNN.Inputs(
             X=X,
@@ -11307,11 +11315,11 @@ def random_normal(
     """
     return _RandomNormal(
         _RandomNormal.Attributes(
-            dtype=AttrDtype(dtype),
-            mean=AttrFloat32(mean),
-            scale=AttrFloat32(scale),
-            seed=AttrFloat32.maybe(seed),
-            shape=AttrInt64s(shape),
+            dtype=AttrDtype(dtype, name="dtype"),
+            mean=AttrFloat32(mean, name="mean"),
+            scale=AttrFloat32(scale, name="scale"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
+            shape=AttrInt64s(shape, name="shape"),
         ),
         _RandomNormal.Inputs(),
     ).outputs.output
@@ -11372,10 +11380,10 @@ def random_normal_like(
     """
     return _RandomNormalLike(
         _RandomNormalLike.Attributes(
-            dtype=AttrDtype.maybe(dtype),
-            mean=AttrFloat32(mean),
-            scale=AttrFloat32(scale),
-            seed=AttrFloat32.maybe(seed),
+            dtype=AttrDtype.maybe(dtype, name="dtype"),
+            mean=AttrFloat32(mean, name="mean"),
+            scale=AttrFloat32(scale, name="scale"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
         ),
         _RandomNormalLike.Inputs(
             input=input,
@@ -11435,11 +11443,11 @@ def random_uniform(
     """
     return _RandomUniform(
         _RandomUniform.Attributes(
-            dtype=AttrDtype(dtype),
-            high=AttrFloat32(high),
-            low=AttrFloat32(low),
-            seed=AttrFloat32.maybe(seed),
-            shape=AttrInt64s(shape),
+            dtype=AttrDtype(dtype, name="dtype"),
+            high=AttrFloat32(high, name="high"),
+            low=AttrFloat32(low, name="low"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
+            shape=AttrInt64s(shape, name="shape"),
         ),
         _RandomUniform.Inputs(),
     ).outputs.output
@@ -11500,10 +11508,10 @@ def random_uniform_like(
     """
     return _RandomUniformLike(
         _RandomUniformLike.Attributes(
-            dtype=AttrDtype.maybe(dtype),
-            high=AttrFloat32(high),
-            low=AttrFloat32(low),
-            seed=AttrFloat32.maybe(seed),
+            dtype=AttrDtype.maybe(dtype, name="dtype"),
+            high=AttrFloat32(high, name="high"),
+            low=AttrFloat32(low, name="low"),
+            seed=AttrFloat32.maybe(seed, name="seed"),
         ),
         _RandomUniformLike.Inputs(
             input=input,
@@ -11666,8 +11674,8 @@ def reduce_l1(
     """
     return _ReduceL1(
         _ReduceL1.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceL1.Inputs(
             data=data,
@@ -11721,8 +11729,8 @@ def reduce_l2(
     """
     return _ReduceL2(
         _ReduceL2.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceL2.Inputs(
             data=data,
@@ -11777,8 +11785,8 @@ def reduce_log_sum(
     """
     return _ReduceLogSum(
         _ReduceLogSum.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceLogSum.Inputs(
             data=data,
@@ -11833,8 +11841,8 @@ def reduce_log_sum_exp(
     """
     return _ReduceLogSumExp(
         _ReduceLogSumExp.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceLogSumExp.Inputs(
             data=data,
@@ -11890,8 +11898,8 @@ def reduce_max(
     """
     return _ReduceMax(
         _ReduceMax.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceMax.Inputs(
             data=data,
@@ -11945,8 +11953,8 @@ def reduce_mean(
     """
     return _ReduceMean(
         _ReduceMean.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceMean.Inputs(
             data=data,
@@ -12001,8 +12009,8 @@ def reduce_min(
     """
     return _ReduceMin(
         _ReduceMin.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceMin.Inputs(
             data=data,
@@ -12056,8 +12064,8 @@ def reduce_prod(
     """
     return _ReduceProd(
         _ReduceProd.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceProd.Inputs(
             data=data,
@@ -12120,8 +12128,10 @@ def reduce_sum(
     """
     return _ReduceSum(
         _ReduceSum.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceSum.Inputs(
             data=data,
@@ -12176,8 +12186,8 @@ def reduce_sum_square(
     """
     return _ReduceSumSquare(
         _ReduceSumSquare.Attributes(
-            axes=AttrInt64s.maybe(axes),
-            keepdims=AttrInt64(keepdims),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _ReduceSumSquare.Inputs(
             data=data,
@@ -12273,7 +12283,7 @@ def reshape(
     """
     return _Reshape(
         _Reshape.Attributes(
-            allowzero=AttrInt64(allowzero),
+            allowzero=AttrInt64(allowzero, name="allowzero"),
         ),
         _Reshape.Inputs(
             data=data,
@@ -12403,12 +12413,16 @@ def resize(
     """
     return _Resize(
         _Resize.Attributes(
-            coordinate_transformation_mode=AttrString(coordinate_transformation_mode),
-            cubic_coeff_a=AttrFloat32(cubic_coeff_a),
-            exclude_outside=AttrInt64(exclude_outside),
-            extrapolation_value=AttrFloat32(extrapolation_value),
-            mode=AttrString(mode),
-            nearest_mode=AttrString(nearest_mode),
+            coordinate_transformation_mode=AttrString(
+                coordinate_transformation_mode, name="coordinate_transformation_mode"
+            ),
+            cubic_coeff_a=AttrFloat32(cubic_coeff_a, name="cubic_coeff_a"),
+            exclude_outside=AttrInt64(exclude_outside, name="exclude_outside"),
+            extrapolation_value=AttrFloat32(
+                extrapolation_value, name="extrapolation_value"
+            ),
+            mode=AttrString(mode, name="mode"),
+            nearest_mode=AttrString(nearest_mode, name="nearest_mode"),
         ),
         _Resize.Inputs(
             X=X,
@@ -12483,8 +12497,8 @@ def reverse_sequence(
     """
     return _ReverseSequence(
         _ReverseSequence.Attributes(
-            batch_axis=AttrInt64(batch_axis),
-            time_axis=AttrInt64(time_axis),
+            batch_axis=AttrInt64(batch_axis, name="batch_axis"),
+            time_axis=AttrInt64(time_axis, name="time_axis"),
         ),
         _ReverseSequence.Inputs(
             input=input,
@@ -12582,12 +12596,14 @@ def roi_align(
     """
     return _RoiAlign(
         _RoiAlign.Attributes(
-            coordinate_transformation_mode=AttrString(coordinate_transformation_mode),
-            mode=AttrString(mode),
-            output_height=AttrInt64(output_height),
-            output_width=AttrInt64(output_width),
-            sampling_ratio=AttrInt64(sampling_ratio),
-            spatial_scale=AttrFloat32(spatial_scale),
+            coordinate_transformation_mode=AttrString(
+                coordinate_transformation_mode, name="coordinate_transformation_mode"
+            ),
+            mode=AttrString(mode, name="mode"),
+            output_height=AttrInt64(output_height, name="output_height"),
+            output_width=AttrInt64(output_width, name="output_width"),
+            sampling_ratio=AttrInt64(sampling_ratio, name="sampling_ratio"),
+            spatial_scale=AttrFloat32(spatial_scale, name="spatial_scale"),
         ),
         _RoiAlign.Inputs(
             X=X,
@@ -12707,7 +12723,7 @@ def stft(
     """
     return _STFT(
         _STFT.Attributes(
-            onesided=AttrInt64(onesided),
+            onesided=AttrInt64(onesided, name="onesided"),
         ),
         _STFT.Inputs(
             signal=signal,
@@ -12941,12 +12957,18 @@ def scan(
     )
     return _Scan(
         _Scan.Attributes(
-            body=AttrGraph(_body_subgraph),
-            num_scan_inputs=AttrInt64(num_scan_inputs),
-            scan_input_axes=AttrInt64s.maybe(scan_input_axes),
-            scan_input_directions=AttrInt64s.maybe(scan_input_directions),
-            scan_output_axes=AttrInt64s.maybe(scan_output_axes),
-            scan_output_directions=AttrInt64s.maybe(scan_output_directions),
+            body=AttrGraph(_body_subgraph, name="body"),
+            num_scan_inputs=AttrInt64(num_scan_inputs, name="num_scan_inputs"),
+            scan_input_axes=AttrInt64s.maybe(scan_input_axes, name="scan_input_axes"),
+            scan_input_directions=AttrInt64s.maybe(
+                scan_input_directions, name="scan_input_directions"
+            ),
+            scan_output_axes=AttrInt64s.maybe(
+                scan_output_axes, name="scan_output_axes"
+            ),
+            scan_output_directions=AttrInt64s.maybe(
+                scan_output_directions, name="scan_output_directions"
+            ),
         ),
         _Scan.Inputs(
             initial_state_and_scan_inputs=initial_state_and_scan_inputs,
@@ -13077,8 +13099,8 @@ def scatter_elements(
     """
     return _ScatterElements(
         _ScatterElements.Attributes(
-            axis=AttrInt64(axis),
-            reduction=AttrString(reduction),
+            axis=AttrInt64(axis, name="axis"),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _ScatterElements.Inputs(
             data=data,
@@ -13199,7 +13221,7 @@ def scatter_nd(
     """
     return _ScatterND(
         _ScatterND.Attributes(
-            reduction=AttrString(reduction),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _ScatterND.Inputs(
             data=data,
@@ -13250,8 +13272,8 @@ def selu(
     """
     return _Selu(
         _Selu.Attributes(
-            alpha=AttrFloat32(alpha),
-            gamma=AttrFloat32(gamma),
+            alpha=AttrFloat32(alpha, name="alpha"),
+            gamma=AttrFloat32(gamma, name="gamma"),
         ),
         _Selu.Inputs(
             X=X,
@@ -13370,7 +13392,7 @@ def sequence_empty(
     """
     return _SequenceEmpty(
         _SequenceEmpty.Attributes(
-            dtype=AttrDtype.maybe(dtype),
+            dtype=AttrDtype.maybe(dtype, name="dtype"),
         ),
         _SequenceEmpty.Inputs(),
     ).outputs.output
@@ -13575,7 +13597,7 @@ def sequence_map(
     )
     return _SequenceMap(
         _SequenceMap.Attributes(
-            body=AttrGraph(_body_subgraph),
+            body=AttrGraph(_body_subgraph, name="body"),
         ),
         _SequenceMap.Inputs(
             input_sequence=input_sequence,
@@ -13663,8 +13685,8 @@ def shape(
     """
     return _Shape(
         _Shape.Attributes(
-            end=AttrInt64.maybe(end),
-            start=AttrInt64(start),
+            end=AttrInt64.maybe(end, name="end"),
+            start=AttrInt64(start, name="start"),
         ),
         _Shape.Inputs(
             data=data,
@@ -13711,8 +13733,8 @@ def shrink(
     """
     return _Shrink(
         _Shrink.Attributes(
-            bias=AttrFloat32(bias),
-            lambd=AttrFloat32(lambd),
+            bias=AttrFloat32(bias, name="bias"),
+            lambd=AttrFloat32(lambd, name="lambd"),
         ),
         _Shrink.Inputs(
             input=input,
@@ -14058,7 +14080,7 @@ def softmax(
     """
     return _Softmax(
         _Softmax.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Softmax.Inputs(
             input=input,
@@ -14177,8 +14199,8 @@ def softmax_cross_entropy_loss(
     """
     return _SoftmaxCrossEntropyLoss(
         _SoftmaxCrossEntropyLoss.Attributes(
-            ignore_index=AttrInt64.maybe(ignore_index),
-            reduction=AttrString(reduction),
+            ignore_index=AttrInt64.maybe(ignore_index, name="ignore_index"),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _SoftmaxCrossEntropyLoss.Inputs(
             scores=scores,
@@ -14294,7 +14316,7 @@ def space_to_depth(
     """
     return _SpaceToDepth(
         _SpaceToDepth.Attributes(
-            blocksize=AttrInt64(blocksize),
+            blocksize=AttrInt64(blocksize, name="blocksize"),
         ),
         _SpaceToDepth.Inputs(
             input=input,
@@ -14346,7 +14368,7 @@ def split(
     """
     return _Split(
         _Split.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _Split.Inputs(
             input=input,
@@ -14413,8 +14435,8 @@ def split_to_sequence(
     """
     return _SplitToSequence(
         _SplitToSequence.Attributes(
-            axis=AttrInt64(axis),
-            keepdims=AttrInt64(keepdims),
+            axis=AttrInt64(axis, name="axis"),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
         ),
         _SplitToSequence.Inputs(
             input=input,
@@ -14556,10 +14578,12 @@ def string_normalizer(
     """
     return _StringNormalizer(
         _StringNormalizer.Attributes(
-            case_change_action=AttrString(case_change_action),
-            is_case_sensitive=AttrInt64(is_case_sensitive),
-            locale=AttrString.maybe(locale),
-            stopwords=AttrStrings.maybe(stopwords),
+            case_change_action=AttrString(
+                case_change_action, name="case_change_action"
+            ),
+            is_case_sensitive=AttrInt64(is_case_sensitive, name="is_case_sensitive"),
+            locale=AttrString.maybe(locale, name="locale"),
+            stopwords=AttrStrings.maybe(stopwords, name="stopwords"),
         ),
         _StringNormalizer.Inputs(
             X=X,
@@ -14848,15 +14872,15 @@ def tf_idf_vectorizer(
     """
     return _TfIdfVectorizer(
         _TfIdfVectorizer.Attributes(
-            max_gram_length=AttrInt64(max_gram_length),
-            max_skip_count=AttrInt64(max_skip_count),
-            min_gram_length=AttrInt64(min_gram_length),
-            mode=AttrString(mode),
-            ngram_counts=AttrInt64s(ngram_counts),
-            ngram_indexes=AttrInt64s(ngram_indexes),
-            pool_int64s=AttrInt64s.maybe(pool_int64s),
-            pool_strings=AttrStrings.maybe(pool_strings),
-            weights=AttrFloat32s.maybe(weights),
+            max_gram_length=AttrInt64(max_gram_length, name="max_gram_length"),
+            max_skip_count=AttrInt64(max_skip_count, name="max_skip_count"),
+            min_gram_length=AttrInt64(min_gram_length, name="min_gram_length"),
+            mode=AttrString(mode, name="mode"),
+            ngram_counts=AttrInt64s(ngram_counts, name="ngram_counts"),
+            ngram_indexes=AttrInt64s(ngram_indexes, name="ngram_indexes"),
+            pool_int64s=AttrInt64s.maybe(pool_int64s, name="pool_int64s"),
+            pool_strings=AttrStrings.maybe(pool_strings, name="pool_strings"),
+            weights=AttrFloat32s.maybe(weights, name="weights"),
         ),
         _TfIdfVectorizer.Inputs(
             X=X,
@@ -14898,7 +14922,7 @@ def thresholded_relu(
     """
     return _ThresholdedRelu(
         _ThresholdedRelu.Attributes(
-            alpha=AttrFloat32(alpha),
+            alpha=AttrFloat32(alpha, name="alpha"),
         ),
         _ThresholdedRelu.Inputs(
             X=X,
@@ -15025,9 +15049,9 @@ def top_k(
     """
     return _TopK(
         _TopK.Attributes(
-            axis=AttrInt64(axis),
-            largest=AttrInt64(largest),
-            sorted=AttrInt64(sorted),
+            axis=AttrInt64(axis, name="axis"),
+            largest=AttrInt64(largest, name="largest"),
+            sorted=AttrInt64(sorted, name="sorted"),
         ),
         _TopK.Inputs(
             X=X,
@@ -15071,7 +15095,7 @@ def transpose(
     """
     return _Transpose(
         _Transpose.Attributes(
-            perm=AttrInt64s.maybe(perm),
+            perm=AttrInt64s.maybe(perm, name="perm"),
         ),
         _Transpose.Inputs(
             data=data,
@@ -15134,7 +15158,7 @@ def trilu(
     """
     return _Trilu(
         _Trilu.Attributes(
-            upper=AttrInt64(upper),
+            upper=AttrInt64(upper, name="upper"),
         ),
         _Trilu.Inputs(
             input=input,
@@ -15314,8 +15338,8 @@ def unique(
     """
     return _Unique(
         _Unique.Attributes(
-            axis=AttrInt64.maybe(axis),
-            sorted=AttrInt64(sorted),
+            axis=AttrInt64.maybe(axis, name="axis"),
+            sorted=AttrInt64(sorted, name="sorted"),
         ),
         _Unique.Inputs(
             X=X,
diff --git a/src/spox/opset/ai/onnx/v18.py b/src/spox/opset/ai/onnx/v18.py
index ab6a22de..ed283c5a 100644
--- a/src/spox/opset/ai/onnx/v18.py
+++ b/src/spox/opset/ai/onnx/v18.py
@@ -1131,7 +1131,7 @@ def center_crop_pad(
     """
     return _CenterCropPad(
         _CenterCropPad.Attributes(
-            axes=AttrInt64s.maybe(axes),
+            axes=AttrInt64s.maybe(axes, name="axes"),
         ),
         _CenterCropPad.Inputs(
             input_data=input_data,
@@ -1222,9 +1222,9 @@ def col2_im(
     """
     return _Col2Im(
         _Col2Im.Attributes(
-            dilations=AttrInt64s.maybe(dilations),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _Col2Im.Inputs(
             input=input,
@@ -1301,8 +1301,8 @@ def group_normalization(
     """
     return _GroupNormalization(
         _GroupNormalization.Attributes(
-            epsilon=AttrFloat32(epsilon),
-            num_groups=AttrInt64(num_groups),
+            epsilon=AttrFloat32(epsilon, name="epsilon"),
+            num_groups=AttrInt64(num_groups, name="num_groups"),
         ),
         _GroupNormalization.Inputs(
             X=X,
@@ -1422,13 +1422,13 @@ def lp_pool(
     """
     return _LpPool(
         _LpPool.Attributes(
-            auto_pad=AttrString(auto_pad),
-            ceil_mode=AttrInt64(ceil_mode),
-            dilations=AttrInt64s.maybe(dilations),
-            kernel_shape=AttrInt64s(kernel_shape),
-            p=AttrInt64(p),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            ceil_mode=AttrInt64(ceil_mode, name="ceil_mode"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            p=AttrInt64(p, name="p"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _LpPool.Inputs(
             X=X,
@@ -1686,7 +1686,7 @@ def pad(
     """
     return _Pad(
         _Pad.Attributes(
-            mode=AttrString(mode),
+            mode=AttrString(mode, name="mode"),
         ),
         _Pad.Inputs(
             data=data,
@@ -1752,8 +1752,10 @@ def reduce_l1(
     """
     return _ReduceL1(
         _ReduceL1.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceL1.Inputs(
             data=data,
@@ -1817,8 +1819,10 @@ def reduce_l2(
     """
     return _ReduceL2(
         _ReduceL2.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceL2.Inputs(
             data=data,
@@ -1883,8 +1887,10 @@ def reduce_log_sum(
     """
     return _ReduceLogSum(
         _ReduceLogSum.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceLogSum.Inputs(
             data=data,
@@ -1949,8 +1955,10 @@ def reduce_log_sum_exp(
     """
     return _ReduceLogSumExp(
         _ReduceLogSumExp.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceLogSumExp.Inputs(
             data=data,
@@ -2016,8 +2024,10 @@ def reduce_max(
     """
     return _ReduceMax(
         _ReduceMax.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceMax.Inputs(
             data=data,
@@ -2081,8 +2091,10 @@ def reduce_mean(
     """
     return _ReduceMean(
         _ReduceMean.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceMean.Inputs(
             data=data,
@@ -2147,8 +2159,10 @@ def reduce_min(
     """
     return _ReduceMin(
         _ReduceMin.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceMin.Inputs(
             data=data,
@@ -2212,8 +2226,10 @@ def reduce_prod(
     """
     return _ReduceProd(
         _ReduceProd.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceProd.Inputs(
             data=data,
@@ -2277,8 +2293,10 @@ def reduce_sum_square(
     """
     return _ReduceSumSquare(
         _ReduceSumSquare.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceSumSquare.Inputs(
             data=data,
@@ -2459,15 +2477,21 @@ def resize(
     """
     return _Resize(
         _Resize.Attributes(
-            antialias=AttrInt64(antialias),
-            axes=AttrInt64s.maybe(axes),
-            coordinate_transformation_mode=AttrString(coordinate_transformation_mode),
-            cubic_coeff_a=AttrFloat32(cubic_coeff_a),
-            exclude_outside=AttrInt64(exclude_outside),
-            extrapolation_value=AttrFloat32(extrapolation_value),
-            keep_aspect_ratio_policy=AttrString(keep_aspect_ratio_policy),
-            mode=AttrString(mode),
-            nearest_mode=AttrString(nearest_mode),
+            antialias=AttrInt64(antialias, name="antialias"),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            coordinate_transformation_mode=AttrString(
+                coordinate_transformation_mode, name="coordinate_transformation_mode"
+            ),
+            cubic_coeff_a=AttrFloat32(cubic_coeff_a, name="cubic_coeff_a"),
+            exclude_outside=AttrInt64(exclude_outside, name="exclude_outside"),
+            extrapolation_value=AttrFloat32(
+                extrapolation_value, name="extrapolation_value"
+            ),
+            keep_aspect_ratio_policy=AttrString(
+                keep_aspect_ratio_policy, name="keep_aspect_ratio_policy"
+            ),
+            mode=AttrString(mode, name="mode"),
+            nearest_mode=AttrString(nearest_mode, name="nearest_mode"),
         ),
         _Resize.Inputs(
             X=X,
@@ -2603,8 +2627,8 @@ def scatter_elements(
     """
     return _ScatterElements(
         _ScatterElements.Attributes(
-            axis=AttrInt64(axis),
-            reduction=AttrString(reduction),
+            axis=AttrInt64(axis, name="axis"),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _ScatterElements.Inputs(
             data=data,
@@ -2741,7 +2765,7 @@ def scatter_nd(
     """
     return _ScatterND(
         _ScatterND.Attributes(
-            reduction=AttrString(reduction),
+            reduction=AttrString(reduction, name="reduction"),
         ),
         _ScatterND.Inputs(
             data=data,
@@ -2800,8 +2824,8 @@ def split(
     """
     return _Split(
         _Split.Attributes(
-            axis=AttrInt64(axis),
-            num_outputs=AttrInt64.maybe(num_outputs),
+            axis=AttrInt64(axis, name="axis"),
+            num_outputs=AttrInt64.maybe(num_outputs, name="num_outputs"),
         ),
         _Split.Inputs(
             input=input,
diff --git a/src/spox/opset/ai/onnx/v19.py b/src/spox/opset/ai/onnx/v19.py
index 5898590e..a21eaebd 100644
--- a/src/spox/opset/ai/onnx/v19.py
+++ b/src/spox/opset/ai/onnx/v19.py
@@ -925,13 +925,13 @@ def average_pool(
     """
     return _AveragePool(
         _AveragePool.Attributes(
-            auto_pad=AttrString(auto_pad),
-            ceil_mode=AttrInt64(ceil_mode),
-            count_include_pad=AttrInt64(count_include_pad),
-            dilations=AttrInt64s.maybe(dilations),
-            kernel_shape=AttrInt64s(kernel_shape),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            auto_pad=AttrString(auto_pad, name="auto_pad"),
+            ceil_mode=AttrInt64(ceil_mode, name="ceil_mode"),
+            count_include_pad=AttrInt64(count_include_pad, name="count_include_pad"),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            kernel_shape=AttrInt64s(kernel_shape, name="kernel_shape"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _AveragePool.Inputs(
             X=X,
@@ -1062,8 +1062,8 @@ def cast(
     """
     return _Cast(
         _Cast.Attributes(
-            saturate=AttrInt64(saturate),
-            to=AttrDtype(to),
+            saturate=AttrInt64(saturate, name="saturate"),
+            to=AttrDtype(to, name="to"),
         ),
         _Cast.Inputs(
             input=input,
@@ -1116,7 +1116,7 @@ def cast_like(
     """
     return _CastLike(
         _CastLike.Attributes(
-            saturate=AttrInt64(saturate),
+            saturate=AttrInt64(saturate, name="saturate"),
         ),
         _CastLike.Inputs(
             input=input,
@@ -1182,13 +1182,13 @@ def constant(
     """
     return _Constant(
         _Constant.Attributes(
-            value=AttrTensor.maybe(value),
-            value_float=AttrFloat32.maybe(value_float),
-            value_floats=AttrFloat32s.maybe(value_floats),
-            value_int=AttrInt64.maybe(value_int),
-            value_ints=AttrInt64s.maybe(value_ints),
-            value_string=AttrString.maybe(value_string),
-            value_strings=AttrStrings.maybe(value_strings),
+            value=AttrTensor.maybe(value, name="value"),
+            value_float=AttrFloat32.maybe(value_float, name="value_float"),
+            value_floats=AttrFloat32s.maybe(value_floats, name="value_floats"),
+            value_int=AttrInt64.maybe(value_int, name="value_int"),
+            value_ints=AttrInt64s.maybe(value_ints, name="value_ints"),
+            value_string=AttrString.maybe(value_string, name="value_string"),
+            value_strings=AttrStrings.maybe(value_strings, name="value_strings"),
         ),
         _Constant.Inputs(),
     ).outputs.output
@@ -1292,12 +1292,12 @@ def deform_conv(
     """
     return _DeformConv(
         _DeformConv.Attributes(
-            dilations=AttrInt64s.maybe(dilations),
-            group=AttrInt64(group),
-            kernel_shape=AttrInt64s.maybe(kernel_shape),
-            offset_group=AttrInt64(offset_group),
-            pads=AttrInt64s.maybe(pads),
-            strides=AttrInt64s.maybe(strides),
+            dilations=AttrInt64s.maybe(dilations, name="dilations"),
+            group=AttrInt64(group, name="group"),
+            kernel_shape=AttrInt64s.maybe(kernel_shape, name="kernel_shape"),
+            offset_group=AttrInt64(offset_group, name="offset_group"),
+            pads=AttrInt64s.maybe(pads, name="pads"),
+            strides=AttrInt64s.maybe(strides, name="strides"),
         ),
         _DeformConv.Inputs(
             X=X,
@@ -1366,7 +1366,7 @@ def dequantize_linear(
     """
     return _DequantizeLinear(
         _DequantizeLinear.Attributes(
-            axis=AttrInt64(axis),
+            axis=AttrInt64(axis, name="axis"),
         ),
         _DequantizeLinear.Inputs(
             x=x,
@@ -1510,8 +1510,8 @@ def if_(
     _then_branch_subgraph: Graph = subgraph((), then_branch)
     return _If(
         _If.Attributes(
-            else_branch=AttrGraph(_else_branch_subgraph),
-            then_branch=AttrGraph(_then_branch_subgraph),
+            else_branch=AttrGraph(_else_branch_subgraph, name="else_branch"),
+            then_branch=AttrGraph(_then_branch_subgraph, name="then_branch"),
         ),
         _If.Inputs(
             cond=cond,
@@ -1703,7 +1703,7 @@ def loop(
     )
     return _Loop(
         _Loop.Attributes(
-            body=AttrGraph(_body_subgraph),
+            body=AttrGraph(_body_subgraph, name="body"),
         ),
         _Loop.Inputs(
             M=M,
@@ -1876,7 +1876,7 @@ def pad(
     """
     return _Pad(
         _Pad.Attributes(
-            mode=AttrString(mode),
+            mode=AttrString(mode, name="mode"),
         ),
         _Pad.Inputs(
             data=data,
@@ -1955,8 +1955,8 @@ def quantize_linear(
     """
     return _QuantizeLinear(
         _QuantizeLinear.Attributes(
-            axis=AttrInt64(axis),
-            saturate=AttrInt64(saturate),
+            axis=AttrInt64(axis, name="axis"),
+            saturate=AttrInt64(saturate, name="saturate"),
         ),
         _QuantizeLinear.Inputs(
             x=x,
@@ -2019,7 +2019,7 @@ def reshape(
     """
     return _Reshape(
         _Reshape.Attributes(
-            allowzero=AttrInt64(allowzero),
+            allowzero=AttrInt64(allowzero, name="allowzero"),
         ),
         _Reshape.Inputs(
             data=data,
@@ -2238,15 +2238,21 @@ def resize(
     """
     return _Resize(
         _Resize.Attributes(
-            antialias=AttrInt64(antialias),
-            axes=AttrInt64s.maybe(axes),
-            coordinate_transformation_mode=AttrString(coordinate_transformation_mode),
-            cubic_coeff_a=AttrFloat32(cubic_coeff_a),
-            exclude_outside=AttrInt64(exclude_outside),
-            extrapolation_value=AttrFloat32(extrapolation_value),
-            keep_aspect_ratio_policy=AttrString(keep_aspect_ratio_policy),
-            mode=AttrString(mode),
-            nearest_mode=AttrString(nearest_mode),
+            antialias=AttrInt64(antialias, name="antialias"),
+            axes=AttrInt64s.maybe(axes, name="axes"),
+            coordinate_transformation_mode=AttrString(
+                coordinate_transformation_mode, name="coordinate_transformation_mode"
+            ),
+            cubic_coeff_a=AttrFloat32(cubic_coeff_a, name="cubic_coeff_a"),
+            exclude_outside=AttrInt64(exclude_outside, name="exclude_outside"),
+            extrapolation_value=AttrFloat32(
+                extrapolation_value, name="extrapolation_value"
+            ),
+            keep_aspect_ratio_policy=AttrString(
+                keep_aspect_ratio_policy, name="keep_aspect_ratio_policy"
+            ),
+            mode=AttrString(mode, name="mode"),
+            nearest_mode=AttrString(nearest_mode, name="nearest_mode"),
         ),
         _Resize.Inputs(
             X=X,
@@ -2480,12 +2486,18 @@ def scan(
     )
     return _Scan(
         _Scan.Attributes(
-            body=AttrGraph(_body_subgraph),
-            num_scan_inputs=AttrInt64(num_scan_inputs),
-            scan_input_axes=AttrInt64s.maybe(scan_input_axes),
-            scan_input_directions=AttrInt64s.maybe(scan_input_directions),
-            scan_output_axes=AttrInt64s.maybe(scan_output_axes),
-            scan_output_directions=AttrInt64s.maybe(scan_output_directions),
+            body=AttrGraph(_body_subgraph, name="body"),
+            num_scan_inputs=AttrInt64(num_scan_inputs, name="num_scan_inputs"),
+            scan_input_axes=AttrInt64s.maybe(scan_input_axes, name="scan_input_axes"),
+            scan_input_directions=AttrInt64s.maybe(
+                scan_input_directions, name="scan_input_directions"
+            ),
+            scan_output_axes=AttrInt64s.maybe(
+                scan_output_axes, name="scan_output_axes"
+            ),
+            scan_output_directions=AttrInt64s.maybe(
+                scan_output_directions, name="scan_output_directions"
+            ),
         ),
         _Scan.Inputs(
             initial_state_and_scan_inputs=initial_state_and_scan_inputs,
@@ -2572,8 +2584,8 @@ def shape(
     """
     return _Shape(
         _Shape.Attributes(
-            end=AttrInt64.maybe(end),
-            start=AttrInt64(start),
+            end=AttrInt64.maybe(end, name="end"),
+            start=AttrInt64(start, name="start"),
         ),
         _Shape.Inputs(
             data=data,
diff --git a/src/spox/opset/ai/onnx/v20.py b/src/spox/opset/ai/onnx/v20.py
index e6bd6b8d..05bc37aa 100644
--- a/src/spox/opset/ai/onnx/v20.py
+++ b/src/spox/opset/ai/onnx/v20.py
@@ -755,7 +755,7 @@ def affine_grid(
     """
     return _AffineGrid(
         _AffineGrid.Attributes(
-            align_corners=AttrInt64(align_corners),
+            align_corners=AttrInt64(align_corners, name="align_corners"),
         ),
         _AffineGrid.Inputs(
             theta=theta,
@@ -803,7 +803,7 @@ def constant_of_shape(
     """
     return _ConstantOfShape(
         _ConstantOfShape.Attributes(
-            value=AttrTensor.maybe(value),
+            value=AttrTensor.maybe(value, name="value"),
         ),
         _ConstantOfShape.Inputs(
             input=input,
@@ -903,8 +903,8 @@ def dft(
     """
     return _DFT(
         _DFT.Attributes(
-            inverse=AttrInt64(inverse),
-            onesided=AttrInt64(onesided),
+            inverse=AttrInt64(inverse, name="inverse"),
+            onesided=AttrInt64(onesided, name="onesided"),
         ),
         _DFT.Inputs(
             input=input,
@@ -954,7 +954,7 @@ def gelu(
     """
     return _Gelu(
         _Gelu.Attributes(
-            approximate=AttrString(approximate),
+            approximate=AttrString(approximate, name="approximate"),
         ),
         _Gelu.Inputs(
             X=X,
@@ -1066,9 +1066,9 @@ def grid_sample(
     """
     return _GridSample(
         _GridSample.Attributes(
-            align_corners=AttrInt64(align_corners),
-            mode=AttrString(mode),
-            padding_mode=AttrString(padding_mode),
+            align_corners=AttrInt64(align_corners, name="align_corners"),
+            mode=AttrString(mode, name="mode"),
+            padding_mode=AttrString(padding_mode, name="padding_mode"),
         ),
         _GridSample.Inputs(
             X=X,
@@ -1137,7 +1137,7 @@ def image_decoder(
     """
     return _ImageDecoder(
         _ImageDecoder.Attributes(
-            pixel_format=AttrString(pixel_format),
+            pixel_format=AttrString(pixel_format, name="pixel_format"),
         ),
         _ImageDecoder.Inputs(
             encoded_stream=encoded_stream,
@@ -1186,8 +1186,8 @@ def isinf(
     """
     return _IsInf(
         _IsInf.Attributes(
-            detect_negative=AttrInt64(detect_negative),
-            detect_positive=AttrInt64(detect_positive),
+            detect_negative=AttrInt64(detect_negative, name="detect_negative"),
+            detect_positive=AttrInt64(detect_positive, name="detect_positive"),
         ),
         _IsInf.Inputs(
             X=X,
@@ -1289,8 +1289,10 @@ def reduce_max(
     """
     return _ReduceMax(
         _ReduceMax.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceMax.Inputs(
             data=data,
@@ -1358,8 +1360,10 @@ def reduce_min(
     """
     return _ReduceMin(
         _ReduceMin.Attributes(
-            keepdims=AttrInt64(keepdims),
-            noop_with_empty_axes=AttrInt64(noop_with_empty_axes),
+            keepdims=AttrInt64(keepdims, name="keepdims"),
+            noop_with_empty_axes=AttrInt64(
+                noop_with_empty_axes, name="noop_with_empty_axes"
+            ),
         ),
         _ReduceMin.Inputs(
             data=data,
@@ -1406,7 +1410,7 @@ def regex_full_match(
     """
     return _RegexFullMatch(
         _RegexFullMatch.Attributes(
-            pattern=AttrString.maybe(pattern),
+            pattern=AttrString.maybe(pattern, name="pattern"),
         ),
         _RegexFullMatch.Inputs(
             X=X,
@@ -1524,8 +1528,8 @@ def string_split(
     """
     return _StringSplit(
         _StringSplit.Attributes(
-            delimiter=AttrString.maybe(delimiter),
-            maxsplit=AttrInt64.maybe(maxsplit),
+            delimiter=AttrString.maybe(delimiter, name="delimiter"),
+            maxsplit=AttrInt64.maybe(maxsplit, name="maxsplit"),
         ),
         _StringSplit.Inputs(
             X=X,
diff --git a/tools/templates/construct.jinja2 b/tools/templates/construct.jinja2
index 589a5773..53f76989 100644
--- a/tools/templates/construct.jinja2
+++ b/tools/templates/construct.jinja2
@@ -23,9 +23,9 @@ return _{{ schema.name }}(
         {% set method = "" %}
     {% endif %}
     {% if attr.attr_constructor == "AttrGraph" %}
-        {% set expr = "AttrGraph{}(_{}_subgraph)".format(method, attr.name) %}
+        {% set expr = 'AttrGraph{}(_{}_subgraph, name="{}")'.format(method, attr.name, attr.name) %}
     {% else %}
-        {% set expr = "{}{}({})".format(attr.attr_constructor, method, attr.name) %}
+        {% set expr = '{}{}({}, name="{}")'.format(attr.attr_constructor, method, attr.name, attr.name) %}
     {% endif %}
     {{ attr.name }}={{ expr }},
 {% endfor %}


Test patch:
diff --git a/tests/test_adapt.py b/tests/test_adapt.py
index 3511963f..8ae22176 100644
--- a/tests/test_adapt.py
+++ b/tests/test_adapt.py
@@ -94,7 +94,7 @@ class Outputs(BaseOutputs):
 
     def squeeze11(_data: Var, _axes: Iterable[int]):
         return Squeeze11(
-            Squeeze11.Attributes(AttrInt64s(_axes)), Squeeze11.Inputs(_data)
+            Squeeze11.Attributes(AttrInt64s(_axes, "axes")), Squeeze11.Inputs(_data)
         ).outputs.squeezed
 
     (data,) = arguments(
diff --git a/tests/test_attr.py b/tests/test_attr.py
index e2c6244e..b1bada98 100644
--- a/tests/test_attr.py
+++ b/tests/test_attr.py
@@ -31,13 +31,13 @@ def test_float_instead_of_int_attr():
     ],
 )
 def test_tensor_does_not_use_raw_data(vals, field, expected):
-    attr = AttrTensor(np.array(vals))
-    pb = attr._to_onnx_deref("foo")
+    attr = AttrTensor(np.array(vals), "foo")
+    pb = attr._to_onnx_deref()
     assert pb.t.raw_data == b""
     assert getattr(pb.t, field) == expected
     assert pb.t.dims == list(np.array(vals).shape)
 
 
 def test_non_ascii_string_attr():
-    AttrString("🐍")._to_onnx(key="foo")
-    AttrStrings(["🐍"])._to_onnx(key="foo")
+    AttrString("🐍", "foo")._to_onnx()
+    AttrStrings(["🐍"], "foo")._to_onnx()
diff --git a/tests/test_function.py b/tests/test_function.py
index 423ec8bd..412253f1 100644
--- a/tests/test_function.py
+++ b/tests/test_function.py
@@ -1,6 +1,6 @@
 import functools
 from dataclasses import dataclass
-from typing import Dict, List
+from typing import Dict, List, Union
 
 import numpy
 import onnx
@@ -10,7 +10,7 @@
 import pytest
 
 import spox.opset.ai.onnx.v17 as op
-from spox._attributes import AttrFloat32, _Ref
+from spox._attributes import Attr, AttrFloat32, _Ref
 from spox._fields import BaseAttributes, BaseInputs, BaseOutputs
 from spox._function import Function, to_function
 from spox._graph import arguments, results
@@ -24,8 +24,8 @@ def linear():
     class LinearFunction(Function):
         @dataclass
         class Attributes(BaseAttributes):
-            slope: AttrFloat32
-            shift: AttrFloat32
+            slope_outer: AttrFloat32
+            shift_outer: AttrFloat32
 
         @dataclass
         class Inputs(BaseInputs):
@@ -41,16 +41,21 @@ class Outputs(BaseOutputs):
         inputs: Inputs
         outputs: Outputs
 
-        def constructor(self, attrs: Dict[str, _Ref], inputs: Inputs) -> Outputs:
+        def constructor(self, attrs: Dict[str, Attr], inputs: Inputs) -> Outputs:
             # FIXME: At some point, attribute references should be properly type-hinted.
-            a = op.constant(value_float=attrs["slope"])  # type: ignore
-            b = op.constant(value_float=attrs["shift"])  # type: ignore
+            a = op.constant(value_float=_Ref(attrs["slope_outer"], outer_name="slope_outer", name="value_float"))  # type: ignore
+            b = op.constant(value_float=_Ref(attrs["shift_outer"], outer_name="shift_outer", name="value_float"))  # type: ignore
             x = inputs.X
             return self.Outputs(op.add(op.mul(a, x), b))
 
-    def linear_inner(x: Var, a: float, b: float) -> Var:
+    def linear_inner(
+        x: Var, a: Union[float, _Ref[float]], b: Union[float, _Ref[float]]
+    ) -> Var:
         return LinearFunction(
-            LinearFunction.Attributes(AttrFloat32(a), AttrFloat32(b)),
+            LinearFunction.Attributes(
+                slope_outer=AttrFloat32(a, "slope_outer"),
+                shift_outer=AttrFloat32(b, "shift_outer"),
+            ),
             LinearFunction.Inputs(x),
         ).outputs.Y
 
@@ -79,12 +84,22 @@ class Outputs(BaseOutputs):
         inputs: Inputs
         outputs: Outputs
 
-        def constructor(self, attrs: Dict[str, _Ref], inputs: Inputs) -> Outputs:
-            return self.Outputs(linear(inputs.X, attrs["slope1"], attrs["shift1"]))  # type: ignore
+        def constructor(self, attrs: Dict[str, Attr], inputs: Inputs) -> Outputs:
+            return self.Outputs(
+                linear(
+                    inputs.X,
+                    _Ref(attrs["slope1"], outer_name="slope1", name="slope_outer"),
+                    _Ref(attrs["shift1"], outer_name="shift1", name="shift_outer"),
+                )
+            )
 
-    def linear_inner(x: Var, a: float, b: float) -> Var:
+    def linear_inner(
+        x: Var, a: Union[float, _Ref[float]], b: Union[float, _Ref[float]]
+    ) -> Var:
         return LinearFunction2(
-            LinearFunction2.Attributes(AttrFloat32(a), AttrFloat32(b)),
+            LinearFunction2.Attributes(
+                slope1=AttrFloat32(a, name="slope1"), shift1=AttrFloat32(b, "shift1")
+            ),
             LinearFunction2.Inputs(x),
         ).outputs.Y
 
@@ -115,12 +130,24 @@ class Outputs(BaseOutputs):
         inputs: Inputs
         outputs: Outputs
 
-        def constructor(self, attrs: Dict[str, _Ref], inputs: Inputs) -> Outputs:
+        def constructor(self, attrs: Dict[str, Attr], inputs: Inputs) -> Outputs:
             x = inputs.X
-            a = op.mul(linear(x, attrs["a3"], attrs["a2"]), op.mul(x, x))  # type: ignore
+            a = op.mul(
+                linear(
+                    x,
+                    _Ref(attrs["a3"], outer_name="a3", name="slope_outer"),
+                    _Ref(attrs["a2"], outer_name="a2", name="shift_outer"),
+                ),
+                op.mul(x, x),
+            )
             b = op.add(
-                op.mul(x, op.constant(value_float=attrs["a1"])),  # type: ignore
-                op.constant(value_float=attrs["a0"]),  # type: ignore
+                op.mul(
+                    x,
+                    op.constant(value_float=_Ref(attrs["a1"], outer_name="a1", name="value_float")),  # type: ignore
+                ),
+                op.constant(
+                    value_float=_Ref(attrs["a0"], outer_name="a0", name="value_float"),  # type: ignore
+                ),
             )
             y = op.add(a, b)
             return self.Outputs(y)
@@ -128,10 +155,10 @@ def constructor(self, attrs: Dict[str, _Ref], inputs: Inputs) -> Outputs:
     def cubic_inner(x: Var, a3: float, a2: float, a1: float, a0: float) -> Var:
         return CubicFunction(
             CubicFunction.Attributes(
-                a3=AttrFloat32(a3),
-                a2=AttrFloat32(a2),
-                a1=AttrFloat32(a1),
-                a0=AttrFloat32(a0),
+                a3=AttrFloat32(a3, name="a3"),
+                a2=AttrFloat32(a2, name="a2"),
+                a1=AttrFloat32(a1, name="a1"),
+                a0=AttrFloat32(a0, name="a0"),
             ),
             CubicFunction.Inputs(X=x),
         ).outputs.Y