@@ -178,6 +178,33 @@ def from_config(
178
178
def _logger (self ) -> logging .Logger :
179
179
return logging .getLogger (__name__ )
180
180
181
+ def __str__ (self ) -> str :
182
+ def _to_str (obj : Any , is_root : bool = False ) -> str :
183
+ """Returns a string representation where
184
+ 1. The root level (i.e. the Dataset.__init__ arguments) are
185
+ formatted like Dataset(key=value).
186
+ 2. Dictionaries have the keys alphabetically sorted recursively.
187
+ 3. None values are not shown.
188
+ """
189
+
190
+ fmt = "{}={}" if is_root else "'{}': {}" # 1
191
+
192
+ if isinstance (obj , dict ):
193
+ sorted_dict = sorted (obj .items (), key = lambda pair : str (pair [0 ])) # 2
194
+
195
+ text = ", " .join (
196
+ fmt .format (key , _to_str (value )) # 2
197
+ for key , value in sorted_dict
198
+ if value is not None # 3
199
+ )
200
+
201
+ return text if is_root else "{" + text + "}" # 1
202
+
203
+ # not a dictionary
204
+ return str (obj )
205
+
206
+ return f"{ type (self ).__name__ } ({ _to_str (self ._describe (), True )} )"
207
+
181
208
@classmethod
182
209
def _load_wrapper (cls , load_func : Callable [[Self ], _DO ]) -> Callable [[Self ], _DO ]:
183
210
@wraps (load_func )
@@ -228,6 +255,12 @@ def save(self: Self, data: _DI) -> None:
228
255
def __init_subclass__ (cls , ** kwargs : Any ) -> None :
229
256
super ().__init_subclass__ (** kwargs )
230
257
258
+ if hasattr (cls , "_load" ) and not cls ._load .__qualname__ .startswith ("Abstract" ):
259
+ cls .load = cls ._load # type: ignore[method-assign]
260
+
261
+ if hasattr (cls , "_save" ) and not cls ._save .__qualname__ .startswith ("Abstract" ):
262
+ cls .save = cls ._save # type: ignore[method-assign]
263
+
231
264
if hasattr (cls , "load" ) and not cls .load .__qualname__ .startswith ("Abstract" ):
232
265
cls .load = cls ._load_wrapper ( # type: ignore[assignment]
233
266
cls .load
@@ -242,6 +275,7 @@ def __init_subclass__(cls, **kwargs: Any) -> None:
242
275
else cls .save .__wrapped__ # type: ignore[attr-defined]
243
276
)
244
277
278
+ @abc .abstractmethod
245
279
def load (self ) -> _DO :
246
280
"""Loads data by delegation to the provided load method.
247
281
@@ -252,21 +286,12 @@ def load(self) -> _DO:
252
286
DatasetError: When underlying load method raises error.
253
287
254
288
"""
289
+ raise NotImplementedError (
290
+ f"'{ self .__class__ .__name__ } ' is a subclass of AbstractDataset and "
291
+ f"it must implement the 'load' method"
292
+ )
255
293
256
- self ._logger .debug ("Loading %s" , str (self ))
257
-
258
- try :
259
- return self ._load ()
260
- except DatasetError :
261
- raise
262
- except Exception as exc :
263
- # This exception handling is by design as the composed data sets
264
- # can throw any type of exception.
265
- message = (
266
- f"Failed while loading data from data set { str (self )} .\n { str (exc )} "
267
- )
268
- raise DatasetError (message ) from exc
269
-
294
+ @abc .abstractmethod
270
295
def save (self , data : _DI ) -> None :
271
296
"""Saves data by delegation to the provided save method.
272
297
@@ -277,59 +302,11 @@ def save(self, data: _DI) -> None:
277
302
DatasetError: when underlying save method raises error.
278
303
FileNotFoundError: when save method got file instead of dir, on Windows.
279
304
NotADirectoryError: when save method got file instead of dir, on Unix.
280
- """
281
-
282
- if data is None :
283
- raise DatasetError ("Saving 'None' to a 'Dataset' is not allowed" )
284
-
285
- try :
286
- self ._logger .debug ("Saving %s" , str (self ))
287
- self ._save (data )
288
- except (DatasetError , FileNotFoundError , NotADirectoryError ):
289
- raise
290
- except Exception as exc :
291
- message = f"Failed while saving data to data set { str (self )} .\n { str (exc )} "
292
- raise DatasetError (message ) from exc
293
-
294
- def __str__ (self ) -> str :
295
- def _to_str (obj : Any , is_root : bool = False ) -> str :
296
- """Returns a string representation where
297
- 1. The root level (i.e. the Dataset.__init__ arguments) are
298
- formatted like Dataset(key=value).
299
- 2. Dictionaries have the keys alphabetically sorted recursively.
300
- 3. None values are not shown.
301
- """
302
-
303
- fmt = "{}={}" if is_root else "'{}': {}" # 1
304
-
305
- if isinstance (obj , dict ):
306
- sorted_dict = sorted (obj .items (), key = lambda pair : str (pair [0 ])) # 2
307
305
308
- text = ", " .join (
309
- fmt .format (key , _to_str (value )) # 2
310
- for key , value in sorted_dict
311
- if value is not None # 3
312
- )
313
-
314
- return text if is_root else "{" + text + "}" # 1
315
-
316
- # not a dictionary
317
- return str (obj )
318
-
319
- return f"{ type (self ).__name__ } ({ _to_str (self ._describe (), True )} )"
320
-
321
- @abc .abstractmethod
322
- def _load (self ) -> _DO :
323
- raise NotImplementedError (
324
- f"'{ self .__class__ .__name__ } ' is a subclass of AbstractDataset and "
325
- f"it must implement the '_load' method"
326
- )
327
-
328
- @abc .abstractmethod
329
- def _save (self , data : _DI ) -> None :
306
+ """
330
307
raise NotImplementedError (
331
308
f"'{ self .__class__ .__name__ } ' is a subclass of AbstractDataset and "
332
- f"it must implement the '_save ' method"
309
+ f"it must implement the 'save ' method"
333
310
)
334
311
335
312
@abc .abstractmethod
@@ -682,7 +659,7 @@ def _get_versioned_path(self, version: str) -> PurePosixPath:
682
659
return self ._filepath / version / self ._filepath .name
683
660
684
661
def load (self ) -> _DO :
685
- return super ().load ()
662
+ return super ().load () # type: ignore[safe-super]
686
663
687
664
@classmethod
688
665
def _save_wrapper (
@@ -724,7 +701,7 @@ def save(self, data: _DI) -> None:
724
701
self ._version_cache .clear ()
725
702
save_version = self .resolve_save_version () # Make sure last save version is set
726
703
try :
727
- super ().save (data )
704
+ super ().save (data ) # type: ignore[safe-super]
728
705
except (FileNotFoundError , NotADirectoryError ) as err :
729
706
# FileNotFoundError raised in Win, NotADirectoryError raised in Unix
730
707
_default_version = "YYYY-MM-DDThh.mm.ss.sssZ"
0 commit comments