Repository: microsoft/torchgeo

Issue:
Determine minimum supported dependency versions
Before releasing, we should determine the minimum supported version of each dependency. We should also consider a test with this version just to make sure it doesn't change.


Discussion:
For testing, I think we can add a matrix field like:
```yaml
matrix:
    dep-version: [oldest, latest]
```
Then, during action setup, `if: ${{ matrix.dep-version == 'oldest' }}`, we can run `sed` on `requirements.txt` to replace `>=` with `==`.

We could also use an `include:` so that we don't have to run every test twice. Still haven't decided if we need to test this just for `pytest` or for all actions. Also not sure how to detect the minimum version without trying everything and seeing what breaks.
https://github.com/pypa/pip/issues/8085 would greatly help with this if it ever gets implemented.
We depend on the latest release candidate of pytorch-lightning (1.4.0) to fix an issue with mypy (the problem where mypy doesn't think a LightningModule's hparams property is indexable)
I would like to differentiate between dependency versions required for torchgeo to function and versions required for things like mypy to pass. We have hundreds of comments across torchgeo that ignore mypy warnings due to missing typing. These will likely change frequently. So the actual version required by torchgeo should be the minimum where the pytest tests pass. For things like mypy/Sphinx, we will always use the latest release. 
Now that we're using `requirements.txt` and dependabot, I think it wouldn't be unreasonable to add a CI test that uses a separate `requirements-min.txt` with the minimum known working versions of our deps. We can then make sure the full range works as expected.
Just discovered that matplotlib does the same thing: https://github.com/matplotlib/matplotlib/blob/main/requirements/testing/minver.txt

Resolving patch:
diff --git a/torchgeo/datasets/advance.py b/torchgeo/datasets/advance.py
--- a/torchgeo/datasets/advance.py
+++ b/torchgeo/datasets/advance.py
@@ -199,7 +199,7 @@ def _load_target(self, path: str) -> Tensor:
                 "scipy is not installed and is required to use this dataset"
             )
 
-        array = wavfile.read(path)[1]
+        array = wavfile.read(path, mmap=True)[1]
         tensor = torch.from_numpy(array)
         tensor = tensor.unsqueeze(0)
         return tensor
diff --git a/torchgeo/datasets/cyclone.py b/torchgeo/datasets/cyclone.py
--- a/torchgeo/datasets/cyclone.py
+++ b/torchgeo/datasets/cyclone.py
@@ -146,7 +146,7 @@ def _load_image(self, directory: str) -> Tensor:
                 # Moved in PIL 9.1.0
                 try:
                     resample = Image.Resampling.BILINEAR
-                except AttributeError:  # pragma: no cover
+                except AttributeError:
                     resample = Image.BILINEAR
                 img = img.resize(size=(self.size, self.size), resample=resample)
             array: "np.typing.NDArray[np.int_]" = np.array(img)
diff --git a/torchgeo/datasets/seco.py b/torchgeo/datasets/seco.py
--- a/torchgeo/datasets/seco.py
+++ b/torchgeo/datasets/seco.py
@@ -183,7 +183,7 @@ def _load_patch(self, scene_name: str, patch_name: str) -> Tensor:
                     # Moved in PIL 9.1.0
                     try:
                         resample = Image.Resampling.BILINEAR
-                    except AttributeError:  # pragma: no cover
+                    except AttributeError:
                         resample = Image.BILINEAR
                     band_data = np.array(
                         pil_image.resize((264, 264), resample=resample)


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -21,7 +21,7 @@ jobs:
         python-version: '3.10'
     - name: Install pip dependencies
       run: |
-        pip install -r requirements.txt
+        pip install -r requirements/requirements.txt
         pip list
     - name: Run mypy checks
       run: mypy .
@@ -62,7 +62,30 @@ jobs:
       if: ${{ runner.os == 'Windows' }}
     - name: Install pip dependencies
       run: |
-        pip install -r requirements.txt
+        pip install -r requirements/requirements.txt
+        pip list
+    - name: Run pytest checks
+      run: pytest --cov=torchgeo --cov-report=xml
+    - name: Report coverage
+      uses: codecov/codecov-action@v2
+  minimum:
+    name: minimum
+    runs-on: ubuntu-latest
+    env:
+      MPLBACKEND: Agg
+    steps:
+    - name: Clone repo
+      uses: actions/checkout@v2
+    - name: Set up python
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.7'
+    - name: Install apt dependencies (Linux)
+      run: sudo apt-get install libgeos-dev libhdf5-dev unrar
+    - name: Install pip dependencies
+      run: |
+        pip install cython numpy==1.17.2  # needed by pycocotools
+        pip install -r requirements/requirements-min.txt
         pip list
     - name: Run pytest checks
       run: pytest --cov=torchgeo --cov-report=xml
diff --git a/tests/datasets/test_advance.py b/tests/datasets/test_advance.py
--- a/tests/datasets/test_advance.py
+++ b/tests/datasets/test_advance.py
@@ -49,7 +49,7 @@ def mocked_import(name: str, *args: Any, **kwargs: Any) -> Any:
         monkeypatch.setattr(builtins, "__import__", mocked_import)
 
     def test_getitem(self, dataset: ADVANCE) -> None:
-        pytest.importorskip("scipy", minversion="0.9.0")
+        pytest.importorskip("scipy", minversion="1.2")
         x = dataset[0]
         assert isinstance(x, dict)
         assert isinstance(x["image"], torch.Tensor)
diff --git a/tests/datasets/test_idtrees.py b/tests/datasets/test_idtrees.py
--- a/tests/datasets/test_idtrees.py
+++ b/tests/datasets/test_idtrees.py
@@ -19,7 +19,7 @@
 import torchgeo.datasets.utils
 from torchgeo.datasets import IDTReeS
 
-pytest.importorskip("pandas", minversion="0.19.1")
+pytest.importorskip("pandas", minversion="0.23.2")
 pytest.importorskip("laspy", minversion="2")
 
 
diff --git a/tests/datasets/test_openbuildings.py b/tests/datasets/test_openbuildings.py
--- a/tests/datasets/test_openbuildings.py
+++ b/tests/datasets/test_openbuildings.py
@@ -24,7 +24,7 @@
     UnionDataset,
 )
 
-pytest.importorskip("pandas", minversion="0.19.1")
+pytest.importorskip("pandas", minversion="0.23.2")
 
 
 class TestOpenBuildings:
diff --git a/tests/datasets/test_so2sat.py b/tests/datasets/test_so2sat.py
--- a/tests/datasets/test_so2sat.py
+++ b/tests/datasets/test_so2sat.py
@@ -15,7 +15,7 @@
 
 from torchgeo.datasets import So2Sat
 
-pytest.importorskip("h5py")
+pytest.importorskip("h5py", minversion="2.6")
 
 
 class TestSo2Sat:
diff --git a/tests/datasets/test_usavars.py b/tests/datasets/test_usavars.py
--- a/tests/datasets/test_usavars.py
+++ b/tests/datasets/test_usavars.py
@@ -18,7 +18,7 @@
 import torchgeo.datasets.utils
 from torchgeo.datasets import USAVars
 
-pytest.importorskip("pandas", minversion="0.19.1")
+pytest.importorskip("pandas", minversion="0.23.2")
 
 
 def download_url(url: str, root: str, *args: str, **kwargs: str) -> None:
diff --git a/tests/datasets/test_zuericrop.py b/tests/datasets/test_zuericrop.py
--- a/tests/datasets/test_zuericrop.py
+++ b/tests/datasets/test_zuericrop.py
@@ -16,7 +16,7 @@
 import torchgeo.datasets.utils
 from torchgeo.datasets import ZueriCrop
 
-pytest.importorskip("h5py")
+pytest.importorskip("h5py", minversion="2.6")
 
 
 def download_url(url: str, root: str, *args: str, **kwargs: str) -> None:
diff --git a/tests/trainers/test_classification.py b/tests/trainers/test_classification.py
--- a/tests/trainers/test_classification.py
+++ b/tests/trainers/test_classification.py
@@ -42,7 +42,7 @@ def test_trainer(
         self, monkeypatch: MonkeyPatch, name: str, classname: Type[LightningDataModule]
     ) -> None:
         if name.startswith("so2sat"):
-            pytest.importorskip("h5py")
+            pytest.importorskip("h5py", minversion="2.6")
 
         conf = OmegaConf.load(os.path.join("tests", "conf", name + ".yaml"))
         conf_dict = OmegaConf.to_object(conf.experiment)
diff --git a/tests/transforms/test_transforms.py b/tests/transforms/test_transforms.py
--- a/tests/transforms/test_transforms.py
+++ b/tests/transforms/test_transforms.py
@@ -11,9 +11,6 @@
 
 from torchgeo.transforms import indices, transforms
 
-# Tests require newer version of Kornia for newer bounding box behavior
-pytest.importorskip("kornia", minversion="0.6.3")
-
 
 @pytest.fixture
 def batch_gray() -> Dict[str, Tensor]: