What is your issue?
Working on migrating the datatree.py module into xarray/core revealed that the DatasetView class, which implements Dataset while disabling methods to mutate the object, breaks Liskov's substitution principle. The type for one of the overloads of DatasetView.__getitem__ is more general than the corresponding Dataset.__getitem__ signature (due to the use of Self in the Dataset signature).
# In Dataset:
class Dataset(...):
...
@overload
def __getitem__(self, key: Iterable[Hashable]) -> Self: ...
The use of Self means that signature inherited from the superclass has a return type of DatasetView, but the DatasetView signature is overridden to have a return type of Dataset (the more generalised parent).
To avoid this, a couple of implementations were attempted:
- A class that tries to intercept the methods that mutate the
Dataset using getattr. This does not catch the __setitem__ method, as it is a Magic Method, and those aren't affected by getattr.
- A
Metaclass that can intercept Magic Methods, too. Implementation was inspired from here. I didn't get it to fully work, and eventually realised this was getting too complicated given the scope of the original problem.
- A mix-in for the mutating methods. I couldn't get this to work in the timescale agreed upon.
- Resorting back to ignoring the
mypy errors for now, so we can proceed with the migration (given that there isn't a significant implementation concern identified from these type issues).
Also note, there is a tangentially-related known mypy error when a property setter accepts an argument of a different type to the property itself (python/mypy#3004). This affects the assignment of Dataset objects to the DataTree.ds property. (Separate issue, but related)
What is your issue?
Working on migrating the datatree.py module into
xarray/corerevealed that theDatasetViewclass, which implementsDatasetwhile disabling methods to mutate the object, breaks Liskov's substitution principle. The type for one of the overloads ofDatasetView.__getitem__is more general than the correspondingDataset.__getitem__signature (due to the use ofSelfin the Dataset signature).The use of
Selfmeans that signature inherited from the superclass has a return type ofDatasetView, but theDatasetViewsignature is overridden to have a return type ofDataset(the more generalised parent).To avoid this, a couple of implementations were attempted:
Datasetusinggetattr. This does not catch the__setitem__method, as it is a Magic Method, and those aren't affected bygetattr.Metaclassthat can intercept Magic Methods, too. Implementation was inspired from here. I didn't get it to fully work, and eventually realised this was getting too complicated given the scope of the original problem.mypyerrors for now, so we can proceed with the migration (given that there isn't a significant implementation concern identified from these type issues).Also note, there is a tangentially-related known
mypyerror when a property setter accepts an argument of a different type to the property itself (python/mypy#3004). This affects the assignment ofDatasetobjects to theDataTree.dsproperty. (Separate issue, but related)