Repository: agrenott/pyhgtmap (language: python)

Issue:
ImportError: cannot import name 'AutoRegister' from 'class_registry' on Ubuntu 24.04
Bonjour Aurélien, 

I tried to install on a fresh Ubuntu 24.04 desktop (up to date @ 2024-09-11, nothing installed), from the git version and pip version, and got the same issue : 

```(my_venv) gis@ubuntu-otm:~$ pyhgtmap
Traceback (most recent call last):
  File "/home/gis/my_venv/bin/pyhgtmap", line 5, in <module>
    from pyhgtmap.main import main
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/main.py", line 10, in <module>
    from pyhgtmap import NASASRTMUtil
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/NASASRTMUtil.py", line 16, in <module>
    from pyhgtmap.sources.pool import Pool
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/sources/__init__.py", line 11, in <module>
    from class_registry import AutoRegister, ClassRegistry
ImportError: cannot import name 'AutoRegister' from 'class_registry' (/home/gis/my_venv/lib/python3.12/site-packages/class_registry/__init__.py)
```
I googled a little bit, found [this](https://class-registry.readthedocs.io/en/latest/advanced_topics.html) and try to replace, in pyhgtmap/sources/__init__.py
```
from class_registry import AutoRegister, ClassRegistry
```
by 
```
from class_registry import ClassRegistry
from class_registry.base import AutoRegister
```

and then I get 

```(my_venv) gis@ubuntu-otm:~$ pyhgtmap
Traceback (most recent call last):
  File "/home/gis/my_venv/bin/pyhgtmap", line 5, in <module>
    from pyhgtmap.main import main
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/main.py", line 10, in <module>
    from pyhgtmap import NASASRTMUtil
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/NASASRTMUtil.py", line 16, in <module>
    from pyhgtmap.sources.pool import Pool
  File "/home/gis/my_venv/lib/python3.12/site-packages/pyhgtmap/sources/__init__.py", line 29, in <module>
    class Source(ABC, metaclass=AutoRegister(SOURCES_TYPES_REGISTRY)):  # type: ignore[misc] # Mypy does not understand dynamically-computed metaclasses
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
````
And then, it's beyond my skills scope, sorry ;-)

Am I doing something wrong? Do I have to force a previous version of classregistry as a temporary workaround?

Thank you,




Discussion / hints:
problem reproduced under windows (with python 3.11.9, 3.12 not working with bind11 something) : 

```PS C:\Windows\system32> python -m pyhgtmap.main
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Program Files\Python311\Lib\site-packages\pyhgtmap\main.py", line 10, in <module>
    from pyhgtmap import NASASRTMUtil
  File "C:\Program Files\Python311\Lib\site-packages\pyhgtmap\NASASRTMUtil.py", line 16, in <module>
    from pyhgtmap.sources.pool import Pool
  File "C:\Program Files\Python311\Lib\site-packages\pyhgtmap\sources\__init__.py", line 10, in <module>
    from class_registry import AutoRegister, ClassRegistry
ImportError: cannot import name 'AutoRegister' from 'class_registry' (C:\Program Files\Python311\Lib\site-packages\class_registry\__init__.py)
PS C:\Windows\system32>```
phx-class-registry 5.0.0 date from 3 days ago, and in the readme : Important, ClassRegistry v5 introduces some changes that can break code that was previously using ClassRegistry v4...

` pip install phx-class-registry==4.1.0` 
to downgrade this dependency did the trick, as a workaround.
Hi,
Thanks for the report and the finding. I'll pin the version to 4.x for the time being, as Python 3.9 support has been dropped since 4.1.0.
Reference for later: https://class-registry.readthedocs.io/en/latest/upgrading_to_v5.html

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index 7e70037..79c4ddf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,7 +28,7 @@ dependencies = [
   "numpy>=1.24.2",
   "nptyping>=2.5.0",
   "npyosmium>=3.6.1",
-  "phx-class-registry>=4.0.6",
+  "phx-class-registry>=4.0.6, <5",
   "pybind11-rdp>=0.1.3",
   "PyDrive2>=1.20.0",
   "scipy>=1.8.0",
@@ -72,7 +72,7 @@ dependencies = [
   "types-beautifulsoup4>=4",
   "mypy>=1.0.1",
   "mypy-extensions~=1.0.0",
-  "ruff>=0.3.0",
+  "ruff>=0.6.4",
 ]
 
 [tool.hatch.envs.default.scripts]


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 2d79159..3a5b282 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,6 +3,6 @@
 from pyhgtmap.configuration import Configuration
 
 
-@pytest.fixture()
+@pytest.fixture
 def configuration() -> Configuration:
     return Configuration()
diff --git a/tests/hgt/test_file.py b/tests/hgt/test_file.py
index dececc2..f254dcb 100644
--- a/tests/hgt/test_file.py
+++ b/tests/hgt/test_file.py
@@ -45,12 +45,12 @@ def toulon_tiles(
     return tiles
 
 
-@pytest.fixture()
+@pytest.fixture
 def toulon_tiles_raw() -> list[HgtTile]:
     return toulon_tiles(smooth_ratio=1)
 
 
-@pytest.fixture()
+@pytest.fixture
 def toulon_tiles_smoothed() -> list[HgtTile]:
     return toulon_tiles(smooth_ratio=3)
 
diff --git a/tests/hgt/test_processor.py b/tests/hgt/test_processor.py
index 2d35fbc..f1ae74d 100644
--- a/tests/hgt/test_processor.py
+++ b/tests/hgt/test_processor.py
@@ -130,7 +130,7 @@ def check_no_id_overlap(osm_files_names: list[str]) -> None:
         ), f"Overlap of ways boundaries {boundaries_1} and {boundaries_2}"
 
 
-@pytest.fixture()
+@pytest.fixture
 def default_options() -> Configuration:
     """Default command line options."""
     return Configuration(
diff --git a/tests/hgt/test_tile.py b/tests/hgt/test_tile.py
index 3e25528..bb4dae8 100644
--- a/tests/hgt/test_tile.py
+++ b/tests/hgt/test_tile.py
@@ -39,17 +39,17 @@ def toulon_tiles(
     return tiles
 
 
-@pytest.fixture()
+@pytest.fixture
 def toulon_tiles_raw() -> list[HgtTile]:
     return toulon_tiles(smooth_ratio=1)
 
 
-@pytest.fixture()
+@pytest.fixture
 def toulon_tiles_smoothed() -> list[HgtTile]:
     return toulon_tiles(smooth_ratio=3)
 
 
-@pytest.fixture()
+@pytest.fixture
 def toulon_tiles_transformed() -> list[HgtTile]:
     """Toulon tiles in 3857 transformed coordinates."""
     # Skip any test using this fixture if GDAL is not installed
diff --git a/tests/sources/test_alos.py b/tests/sources/test_alos.py
index 3809b14..bf44806 100644
--- a/tests/sources/test_alos.py
+++ b/tests/sources/test_alos.py
@@ -59,7 +59,7 @@ def get_alos_zipped_file(alos_area: str) -> BytesIO:
     return output_file
 
 
-@pytest.fixture()
+@pytest.fixture
 def alos_configuration(configuration: Configuration) -> Configuration:
     alos_specific = AlosConfiguration()
     configuration.add_sub_config("alos", alos_specific)
diff --git a/tests/sources/test_pool.py b/tests/sources/test_pool.py
index 80c1f60..cb4e8c6 100644
--- a/tests/sources/test_pool.py
+++ b/tests/sources/test_pool.py
@@ -60,7 +60,7 @@ def download_missing_file(
                 pass
 
 
-@pytest.fixture()
+@pytest.fixture
 def pool(configuration: Configuration) -> Pool:
     return Pool("root_dir", "cfg_dir", configuration)
 
diff --git a/tests/sources/test_sonny.py b/tests/sources/test_sonny.py
index f2029c1..dec58aa 100644
--- a/tests/sources/test_sonny.py
+++ b/tests/sources/test_sonny.py
@@ -13,14 +13,14 @@
 from pyhgtmap.sources.sonny import CLIENT_SECRET_FILE, SAVED_CREDENTIALS_FILE, Sonny
 
 
-@pytest.fixture()
+@pytest.fixture
 def gauth_mock() -> Generator[MagicMock, None, None]:
     """Mock pyhgtmap.sources.sonny.GoogleAuth"""
     with patch("pyhgtmap.sources.sonny.GoogleAuth") as gauth_mock:
         yield gauth_mock
 
 
-@pytest.fixture()
+@pytest.fixture
 def gdrive_mock() -> Generator[MagicMock, None, None]:
     """Mock pyhgtmap.sources.sonny.GoogleDrive"""
     with patch("pyhgtmap.sources.sonny.GoogleDrive") as gdrive_mock:
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index a16bcbf..ae250c5 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -3,7 +3,7 @@
 from pyhgtmap.configuration import NestedConfig
 
 
-@pytest.fixture()
+@pytest.fixture
 def config() -> NestedConfig:
     return NestedConfig()
 
diff --git a/tests/test_main.py b/tests/test_main.py
index 7eb17e8..c3b50cc 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -16,14 +16,14 @@
     from pyhgtmap.configuration import Configuration
 
 
-@pytest.fixture()
+@pytest.fixture
 def HgtFilesProcessor_mock() -> Generator[MagicMock, Any, None]:
     """Fixture mocking pyhgtmap.main.HgtFilesProcessor."""
     with patch("pyhgtmap.main.HgtFilesProcessor") as mock:
         yield mock
 
 
-@pytest.fixture()
+@pytest.fixture
 def NASASRTMUtil_mock() -> Generator[MagicMock, Any, None]:
     """Fixture mocking pyhgtmap.main.NASASRTMUtil."""
     with patch("pyhgtmap.main.NASASRTMUtil") as mock:
diff --git a/tests/test_output.py b/tests/test_output.py
index 3737389..24d3f2f 100644
--- a/tests/test_output.py
+++ b/tests/test_output.py
@@ -50,7 +50,7 @@ def arrays_from_lists(
     ]
 
 
-@pytest.fixture()
+@pytest.fixture
 def tile_contours() -> TileContours:
     return TileContours(
         nb_nodes=8,
@@ -75,18 +75,18 @@ def tile_contours() -> TileContours:
     )
 
 
-@pytest.fixture()
+@pytest.fixture
 def bounding_box() -> BBox:
     """Bounding box of all fake data nodes"""
     return BBox(1, 1, 4, 2)
 
 
-@pytest.fixture()
+@pytest.fixture
 def elev_classifier() -> Callable[[int], str]:
     return make_elev_classifier(100, 50)
 
 
-@pytest.fixture()
+@pytest.fixture
 def elevations() -> Iterable[int]:
     return [0, 50, 100, 150]