Repository: shapely/shapely (language: python)

Issue:
Deprecate the shapely.geos module
To do item for Shapely 1.8: find a way to raise deprecation warnings for functionality in the `shapely.geos` module (as this will be completely removed in Shapely 2.0). 
Although it's not really documented / recommended, it will be used in practice I think.

Eg the WKTReader / WKTWriter (eg https://stackoverflow.com/a/29548439/653364), the `lgeos` object to directly access GEOS functions (eg https://gis.stackexchange.com/a/226912/9828, we also did this in geopandas https://github.com/geopandas/geopandas/blob/8be1fd8b2a0b7691031d59b3a54275304db8aaa9/geopandas/io/sql.py#L58)


Discussion / hints:
Although the difficulty for users might be that the alternative will not yet be available in Shapely 1.8, but only in 2.0, so they can't really yet "solve" the warning.
There's nothing to be done for users who are tapping into the lgeos object, it's going to be removed.

There are users of the format readers/writers, so let's try to move them to shapely.wkt?
Agree to move wkt and wbk reader/writers classes/defaults to wkt and wkb modules.

Another consideration is to add a wrapper module `shapely.geos` in Shapely 2.0 to expose a few properties/classes, depending on how easy/difficult it is to show deprecation warnings.
> There's nothing to be done for users who are tapping into the lgeos object, it's going to be removed.

Yes, I know there won't be anything equivalent in Shapely 2.0, I don't care about that (in the end, the reasons people needed it now should be covered by new functionality). 
But the main reason I opened the ticket was to see if we can / want to warn in advance about this fact that it will disappear.

> There are users of the format readers/writers, so let's try to move them to shapely.wkt?

For the WKT/WKB reading and writing, all the functionality for which you now required to use the `shapely.geos` readers and writers directly is already covered by PyGEOS functionality (eg all options from GEOS are exposed, https://pygeos.readthedocs.io/en/latest/io.html#pygeos.io.to_wkb). So I think that can be integrated in the shapely.wkt/wkb functions, without actually adding those reader/writer classes to those modules (all functionality can be exposed in the functions I think).

Another example usage I linked too was `shapely.geos.lgeos.GEOSGetSRID`, but also that is already covered in PyGEOS with a ufunc (https://pygeos.readthedocs.io/en/latest/geometry.html#pygeos.geometry.get_srid)

> Another consideration is to add a wrapper module shapely.geos in Shapely 2.0 to expose a few properties/classes

Are you thinking specifically for the WKT/WKB readers/writers?

We could also make sure that all options for which users now might have used those classes are fully exposed and documented in shapely.wkt/wkb *for Shapely 1.8*, so users can actually already migrate away from using shapely.geos directly for this use case before it gets removed.
> Are you thinking specifically for the WKT/WKB readers/writers?
> 
> We could also make sure that all options for which users now might have used those classes are fully exposed and documented in shapely.wkt/wkb _for Shapely 1.8_, so users can actually already migrate away from using shapely.geos directly for this use case before it gets removed.

Yes. For what it's worth, decisions where these modules should be put were made back in #63, so they were put in `shapyley.geos`, rather than `shapely.wkt` and `shapely.wkb`.

Fix patch:
diff --git a/CHANGES.txt b/CHANGES.txt
index c14f04e11..631394e03 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -42,6 +42,12 @@ Breaking changes in GEOS 3.12:
 - The WKT representation of a MULTIPOINT changed from for example "MULTIPOINT (0 0, 1 1)"
   to "MULTIPOINT ((0 0), (1 1))". (#1885)
 
+Deprecations:
+
+- The ``shapely.geos`` module is deprecated. All GEOS-version related attributes are
+  available directly from the top-level ``shapely`` namespace as well (already since
+  shapely 2.0) (#2145).
+
 Packaging:
 
 - Binary wheels are now built for musllinux (Alpine) x86_64 platforms (#1996).
diff --git a/shapely/geos.py b/shapely/geos.py
index 5fa3bf227..3667365ef 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -1,7 +1,17 @@
 """Proxies for libgeos, GEOS-specific exceptions, and utilities."""
 
+import warnings
+
 import shapely
 
+warnings.warn(
+    "The 'shapely.geos' module is deprecated, and will be removed in a future version. "
+    "All attributes of 'shapely.geos' are available directly from the top-level "
+    "'shapely' namespace (since shapely 2.0.0).",
+    DeprecationWarning,
+    stacklevel=2,
+)
+
 geos_version_string = shapely.geos_capi_version_string
 geos_version = shapely.geos_version
 geos_capi_version = shapely.geos_capi_version


Test patch:
diff --git a/shapely/tests/geometry/test_format.py b/shapely/tests/geometry/test_format.py
index d32c50739..797b68481 100644
--- a/shapely/tests/geometry/test_format.py
+++ b/shapely/tests/geometry/test_format.py
@@ -1,7 +1,6 @@
 import pytest
 
-from shapely import Point, Polygon
-from shapely.geos import geos_version
+from shapely import Point, Polygon, geos_version
 
 
 def test_format_invalid():
diff --git a/shapely/tests/legacy/__init__.py b/shapely/tests/legacy/__init__.py
index f77e7a5db..93f2ea963 100644
--- a/shapely/tests/legacy/__init__.py
+++ b/shapely/tests/legacy/__init__.py
@@ -2,7 +2,7 @@
 
 import numpy
 
-from shapely.geos import geos_version_string
+from shapely import geos_version_string
 
 # Show some diagnostic information; handy for CI
 print("Python version: " + sys.version.replace("\n", " "))
diff --git a/shapely/tests/legacy/test_create_inconsistent_dimensionality.py b/shapely/tests/legacy/test_create_inconsistent_dimensionality.py
index 04d11a9c6..d87fba2a8 100644
--- a/shapely/tests/legacy/test_create_inconsistent_dimensionality.py
+++ b/shapely/tests/legacy/test_create_inconsistent_dimensionality.py
@@ -8,10 +8,10 @@
 
 import pytest
 
+import shapely
 from shapely import wkt
 from shapely.errors import GEOSException
 from shapely.geometry import LineString, Polygon, shape
-from shapely.geos import geos_version
 
 geojson_cases = [
     {"type": "LineString", "coordinates": [[1, 1, 1], [2, 2]]},
@@ -208,7 +208,7 @@ def test_create_directly(constructor, args):
 
 @pytest.mark.parametrize("wkt_geom,expected", wkt_cases)
 def test_create_from_wkt(wkt_geom, expected):
-    if geos_version >= (3, 12, 0):
+    if shapely.geos_version >= (3, 12, 0):
         # https://github.com/shapely/shapely/issues/1541
         with pytest.raises(GEOSException):
             wkt.loads(wkt_geom)
diff --git a/shapely/tests/legacy/test_pickle.py b/shapely/tests/legacy/test_pickle.py
index 911a5778e..641f7b7c1 100644
--- a/shapely/tests/legacy/test_pickle.py
+++ b/shapely/tests/legacy/test_pickle.py
@@ -74,7 +74,7 @@ def test_unpickle_pre_20(fname):
 
     shapely_version = shapely.__version__
     print(shapely_version)
-    print(shapely.geos.geos_version)
+    print(shapely.geos_version)
 
     for name, geom in TEST_DATA.items():
         with open(datadir / f"{name}_{shapely_version}.pickle", "wb") as f: