@@ -122,6 +122,75 @@ def __array_wrap__(self, obj, context=None, return_scalar=None):
122122 ret = ma .MaskedArray .__array_wrap__ (self , obj , context = context , return_scalar = return_scalar )
123123 return ret
124124
125+ # ==============================================================================================================
126+ ############################ Descriptor Protocol ################################################
127+ # ==============================================================================================================
128+
129+ def __set_name__ (self , owner , name ):
130+ """Record the attribute name when the descriptor is assigned as a class attribute.
131+
132+ Args:
133+ owner (type):
134+ The class that owns this descriptor.
135+ name (str):
136+ The attribute name used for this descriptor on the owner class.
137+ """
138+ self ._attr_name = name
139+ self ._private_name = f"_{ name } "
140+
141+ def __get__ (self , obj , objtype = None ):
142+ """Return the DataArray stored on the owner instance (descriptor getter).
143+
144+ Args:
145+ obj: The owner instance, or ``None`` when accessed on the class itself.
146+ objtype: The owner class.
147+
148+ Returns:
149+ DataArray: The stored data array, guaranteed to be at least 2-dimensional,
150+ or this descriptor instance when accessed on the class.
151+ """
152+ if obj is None :
153+ return self
154+ try :
155+ return np .atleast_2d (object .__getattribute__ (obj , self ._private_name ))
156+ except AttributeError :
157+ return np .atleast_2d (DataArray ([]))
158+
159+ def __set__ (self , obj , value ):
160+ """Store *value* as a :class:`DataArray` on the owner instance (descriptor setter).
161+
162+ The setter normalises the incoming value so that it is always a 2-D
163+ :class:`DataArray`. Column headers and ``setas`` assignments are
164+ preserved from the existing data when the number of columns matches.
165+
166+ Args:
167+ obj: The owner instance.
168+ value (array-like): New data to store.
169+
170+ Raises:
171+ ValueError: If *value* has more than 2 dimensions.
172+ """
173+ nv = value
174+ if not nv .shape : # nv is a scalar - make it a 2D array
175+ nv = ma .atleast_2d (nv )
176+ elif nv .ndim == 1 : # nv is a vector - make it a 2D array
177+ nv = ma .atleast_2d (nv ).T
178+ elif nv .ndim > 2 :
179+ raise ValueError (f"DataFile.data should be no more than 2 dimensional not shape { nv .shape } " )
180+ try :
181+ old_data = object .__getattribute__ (obj , self ._private_name )
182+ except AttributeError :
183+ old_data = DataArray ([])
184+ if not isinstance (nv , DataArray ): # nv isn't a DataArray, so preserve setas
185+ nv = DataArray (nv )
186+ nv ._setas = old_data ._setas .clone
187+ elif old_data .ndim >= 2 and nv .shape [1 ] == old_data .shape [1 ]: # same columns - preserve column_headers and setas
188+ ch = old_data .column_headers
189+ nv ._setas = old_data ._setas .clone
190+ nv .column_headers = ch
191+ nv ._setas .shape = nv .shape
192+ object .__setattr__ (obj , self ._private_name , nv )
193+
125194 def _prepare_index (self , ix ):
126195 """Mangle an indexing argument into a standard tuple form."""
127196 match ix :
0 commit comments