From 6b31f0988212898428b771f446cf37f8b5696893 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 8 Oct 2025 15:21:35 -0400 Subject: [PATCH 1/5] initial attempt at Python 3.14 support --- .github/workflows/tests.yaml | 1 + ci/envs/314-latest.yaml | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 ci/envs/314-latest.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3d593192..641d4c04 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,6 +33,7 @@ jobs: - ci/envs/312-latest.yaml - ci/envs/313-latest.yaml - ci/envs/313-dev.yaml + - ci/envs/314-latest.yaml include: - environment-file: ci/envs/313-latest.yaml os: macos-13 # Intel diff --git a/ci/envs/314-latest.yaml b/ci/envs/314-latest.yaml new file mode 100644 index 00000000..d8977caf --- /dev/null +++ b/ci/envs/314-latest.yaml @@ -0,0 +1,35 @@ +name: test +channels: + - conda-forge +dependencies: + - python=3.14 + - geopandas + - inequality + - libpysal>=4.12.0 + - mapclassify + - networkx + - osmnx + - packaging + - pandas!=1.5.0 + - shapely>=2 + - esda + - tqdm + - numba + - rioxarray + - xvec + # testing + - codecov + - pytest + - pytest-cov + # user guide testing + - dask + - inequality + - jupyter + - matplotlib + - osmnx + - clustergram + - bokeh + - geopy + - ipywidgets + - Iprogress + - pytest-doctestplus From 5cc7fe25d3825d61537c716d8a106b802995bbcd Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 8 Oct 2025 15:40:56 -0400 Subject: [PATCH 2/5] bump oldest dependency version - #715 --- ci/envs/311-oldest.yaml | 10 +++++----- docs/install.rst | 6 +++--- pyproject.toml | 13 +++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ci/envs/311-oldest.yaml b/ci/envs/311-oldest.yaml index 5130eccc..a46176f4 100644 --- a/ci/envs/311-oldest.yaml +++ b/ci/envs/311-oldest.yaml @@ -3,16 +3,16 @@ channels: - conda-forge dependencies: - python=3.11 - - geopandas=0.14 + - geopandas=1.0 - inequality - libpysal=4.12.0 - mapclassify - - networkx=3.2 - - numpy=1.25 + - networkx=3.3 + - numpy=2.0 - packaging - - pandas=2.0 + - pandas=2.2 - shapely=2.0 - - tqdm=4.65 + - tqdm=4.67 - numba=0.58 - fiona=1.10 # testing diff --git a/docs/install.rst b/docs/install.rst index b10697bb..5fbaaf8e 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -84,14 +84,14 @@ Dependencies Required dependencies: -- `geopandas`_ (>= 0.12.0) +- `geopandas`_ (>= 1.0) - `libpysal`_ (>= 4.12.0) - `networkx`_ - `tqdm`_ Some functions also depend on additional packages, which are optional: -- `mapclassify`_ (>= 2.4.2) +- `mapclassify`_ - `inequality`_ - `numba`_ - `esda`_ @@ -111,7 +111,7 @@ Some functions also depend on additional packages, which are optional: .. _numba: https://numba.pydata.org -.. _tqdm: http://networkx.github.io +.. _tqdm: https://tqdm.github.io .. _pysal: http://pysal.org diff --git a/pyproject.toml b/pyproject.toml index 969d8cda..e7665010 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,13 +34,14 @@ classifiers = [ ] requires-python = ">=3.11" dependencies = [ - "geopandas>=0.14.0", + "geopandas>=1.0", "libpysal>=4.12.0", - "networkx>=3.2", + "networkx>=3.3", + "numpy>=2.0", "packaging", - "pandas>=2.0", - "shapely>=2", - "tqdm>=4.65", + "pandas>=2.2", + "shapely>=2.0", + "tqdm>=4.67", ] [project.urls] @@ -49,7 +50,7 @@ Repository = "https://github.com/pysal/momepy" [project.optional-dependencies] plus = [ - "numba", + "numba>=0.58", "inequality", "mapclassify", "esda", From 9f554fda4a850195d98b1cea17071d63f71cb215 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 8 Oct 2025 15:43:54 -0400 Subject: [PATCH 3/5] remove 314 stuff snuck in from #716 --- .github/workflows/tests.yaml | 1 - ci/envs/314-latest.yaml | 35 ----------------------------------- 2 files changed, 36 deletions(-) delete mode 100644 ci/envs/314-latest.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 641d4c04..3d593192 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,7 +33,6 @@ jobs: - ci/envs/312-latest.yaml - ci/envs/313-latest.yaml - ci/envs/313-dev.yaml - - ci/envs/314-latest.yaml include: - environment-file: ci/envs/313-latest.yaml os: macos-13 # Intel diff --git a/ci/envs/314-latest.yaml b/ci/envs/314-latest.yaml deleted file mode 100644 index d8977caf..00000000 --- a/ci/envs/314-latest.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: test -channels: - - conda-forge -dependencies: - - python=3.14 - - geopandas - - inequality - - libpysal>=4.12.0 - - mapclassify - - networkx - - osmnx - - packaging - - pandas!=1.5.0 - - shapely>=2 - - esda - - tqdm - - numba - - rioxarray - - xvec - # testing - - codecov - - pytest - - pytest-cov - # user guide testing - - dask - - inequality - - jupyter - - matplotlib - - osmnx - - clustergram - - bokeh - - geopy - - ipywidgets - - Iprogress - - pytest-doctestplus From 0d26e0ca94f7756b214e2701cebea073fa095b31 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 8 Oct 2025 15:45:47 -0400 Subject: [PATCH 4/5] bump oldest numba --- ci/envs/311-oldest.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/envs/311-oldest.yaml b/ci/envs/311-oldest.yaml index a46176f4..f01358fc 100644 --- a/ci/envs/311-oldest.yaml +++ b/ci/envs/311-oldest.yaml @@ -13,7 +13,7 @@ dependencies: - pandas=2.2 - shapely=2.0 - tqdm=4.67 - - numba=0.58 + - numba=0.60 - fiona=1.10 # testing - codecov diff --git a/pyproject.toml b/pyproject.toml index e7665010..8016be03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ Repository = "https://github.com/pysal/momepy" [project.optional-dependencies] plus = [ - "numba>=0.58", + "numba>=0.60", "inequality", "mapclassify", "esda", From 47457d58d1c85b69c094014118038a482f4d60f5 Mon Sep 17 00:00:00 2001 From: James Gaboardi Date: Wed, 8 Oct 2025 16:03:52 -0400 Subject: [PATCH 5/5] remove unneeded lower bound logic --- momepy/diversity.py | 7 ------- momepy/elements.py | 11 +++-------- momepy/tests/test_diversity.py | 27 --------------------------- momepy/tests/test_intensity.py | 7 ------- 4 files changed, 3 insertions(+), 49 deletions(-) diff --git a/momepy/diversity.py b/momepy/diversity.py index 9015bae3..1156be77 100644 --- a/momepy/diversity.py +++ b/momepy/diversity.py @@ -3,7 +3,6 @@ from libpysal.graph import Graph from libpysal.graph._utils import _percentile_filtration_grouper from numpy.typing import NDArray -from packaging.version import Version from pandas import DataFrame, Series try: @@ -162,9 +161,6 @@ def describe_agg( 6.0 652.432194 32.829824 """ # noqa: E501 - if Version(pd.__version__) <= Version("2.1.0"): - raise ImportError("pandas 2.1.0 or newer is required to use this function.") - # series indice needs renaming, since multiindices # without explicit names cannot be joined if isinstance(y, np.ndarray): @@ -289,9 +285,6 @@ def describe_reached_agg( 4 683.514930 NaN """ # noqa: E501 - if Version(pd.__version__) <= Version("2.1.0"): - raise ImportError("pandas 2.1.0 or newer is required to use this function.") - # series indice needs renaming, since multiindices # without explicit names cannot be joined if isinstance(y, np.ndarray): diff --git a/momepy/elements.py b/momepy/elements.py index c0abe3da..b3e34f90 100644 --- a/momepy/elements.py +++ b/momepy/elements.py @@ -13,7 +13,6 @@ from shapely.geometry.base import BaseGeometry from shapely.ops import polygonize -GPD_GE_10 = Version(gpd.__version__) >= Version("1.0dev") SHPLY_GE_210 = Version(shapely.__version__) >= Version("2.1.0") __all__ = [ @@ -127,7 +126,7 @@ def morphological_tessellation( ) if isinstance(clip, GeoSeries | GeoDataFrame): - clip = clip.union_all() if GPD_GE_10 else clip.unary_union + clip = clip.union_all() mt = voronoi_frames( geometry, @@ -772,11 +771,7 @@ def buffered_limit( elif not isinstance(buffer, int | float): raise ValueError("`buffer` must be either 'adaptive' or a number.") - return ( - gdf.buffer(buffer, **kwargs).union_all() - if GPD_GE_10 - else gdf.buffer(buffer, **kwargs).unary_union - ) + return gdf.buffer(buffer, **kwargs).union_all() def get_network_ratio(df, edges, initial_buffer=500): @@ -921,7 +916,7 @@ def enclosures( barriers = pd.concat([primary_barriers.geometry, limit_b.geometry]) else: barriers = primary_barriers - unioned = barriers.union_all() if GPD_GE_10 else barriers.unary_union + unioned = barriers.union_all() polygons = polygonize(unioned) enclosures = gpd.GeoSeries(list(polygons), crs=primary_barriers.crs) diff --git a/momepy/tests/test_diversity.py b/momepy/tests/test_diversity.py index 52016cc5..c8694cf3 100644 --- a/momepy/tests/test_diversity.py +++ b/momepy/tests/test_diversity.py @@ -3,15 +3,12 @@ import pandas as pd import pytest from libpysal.graph import Graph -from packaging.version import Version from pandas.testing import assert_frame_equal, assert_series_equal import momepy as mm from .conftest import assert_frame_result, assert_result -PD_210 = Version(pd.__version__) >= Version("2.1.0") - class TestDescribe: def setup_method(self): @@ -394,9 +391,6 @@ def test_unique(self): un_nan_drop, un_nan_drop_expected, self.df_tessellation, check_names=False ) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_describe_agg(self): df = mm.describe_agg( self.df_buildings["area"], @@ -469,9 +463,6 @@ def test_describe_agg(self): assert_result(df["sum"], expected_fl_area_sum, result_index) assert_result(df["mean"], expected_fl_area_mean, result_index) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_describe_cols(self): df = mm.describe_agg( self.df_buildings["area"], @@ -480,9 +471,6 @@ def test_describe_cols(self): ) assert list(df.columns) == ["min", "max"] - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_describe_reached_agg(self): df_sw = mm.describe_reached_agg( self.df_buildings["fl_area"], self.df_buildings["nID"], graph=self.graph_sw @@ -512,9 +500,6 @@ def test_describe_reached_agg(self): filtered_df["count"], filtered_expected, self.df_streets, check_names=False ) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_describe_reached_input_equality(self): island_result_df = mm.describe_agg( self.df_buildings["area"], self.df_buildings["nID"] @@ -529,9 +514,6 @@ def test_describe_reached_input_equality(self): island_result_df.values, island_result_ndarray.values, equal_nan=True ) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_describe_reached_cols(self): df = mm.describe_reached_agg( self.df_buildings["fl_area"], @@ -542,9 +524,6 @@ def test_describe_reached_cols(self): ) assert list(df.columns) == ["min", "max"] - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_na_results(self): nan_areas = self.df_buildings["area"] nan_areas.iloc[range(0, len(self.df_buildings), 3),] = np.nan @@ -614,9 +593,6 @@ def test_density(self): check_names=False, ) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_unweighted_percentile(self): perc = mm.percentile(self.df_tessellation["area"], self.diversity_graph) perc_expected = { @@ -662,9 +638,6 @@ def test_unweighted_percentile(self): perc = mm.percentile(self.df_tessellation["area"].iloc[:100], graph) assert perc.loc[0].isna().all() - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_distance_decay_linearly_weighted_percentiles(self): # setup weight decay graph diff --git a/momepy/tests/test_intensity.py b/momepy/tests/test_intensity.py index 17d7e877..9d826db9 100644 --- a/momepy/tests/test_intensity.py +++ b/momepy/tests/test_intensity.py @@ -2,17 +2,13 @@ import networkx as nx import numpy as np import pandas as pd -import pytest from libpysal.graph import Graph -from packaging.version import Version from pandas.testing import assert_series_equal import momepy as mm from .conftest import assert_result -PD_210 = Version(pd.__version__) >= Version("2.1.0") - class TestIntensity: def setup_method(self): @@ -95,9 +91,6 @@ def test_node_density(self): assert_series_equal(alternative_density, density) assert_series_equal(alternative_weighted, weighted) - @pytest.mark.skipif( - not PD_210, reason="aggregation is different in previous pandas versions" - ) def test_area_ratio(self): def area_ratio(overlay, covering, agg_key): res = mm.describe_agg(covering, agg_key)