Skip to content

Commit

Permalink
geopandas: improvements to publicly exported private symbols (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
hamdanal committed Jul 27, 2024
1 parent 9a7e431 commit ac9126d
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 88 deletions.
27 changes: 10 additions & 17 deletions stubs/geopandas-stubs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import numpy
import pandas

import geopandas

from ._config import options as options
from ._exports import (
gpd as gpd,
list_layers as list_layers,
np as np,
pd as pd,
read_feather as read_feather,
read_file as read_file,
read_parquet as read_parquet,
read_postgis as read_postgis,
)
from .array import points_from_xy as points_from_xy
from .geodataframe import GeoDataFrame as GeoDataFrame
from .geoseries import GeoSeries as GeoSeries
from .io.arrow import _read_feather, _read_parquet
from .io.file import _list_layers, _read_file
from .io.sql import _read_postgis
from .tools import clip as clip, overlay as overlay, sjoin as sjoin, sjoin_nearest as sjoin_nearest
from .tools._show_versions import show_versions as show_versions

list_layers = _list_layers
read_file = _read_file
read_feather = _read_feather
read_parquet = _read_parquet
read_postgis = _read_postgis

gpd = geopandas
np = numpy
pd = pandas
__version__: str
21 changes: 21 additions & 0 deletions stubs/geopandas-stubs/_decorator.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from collections.abc import Callable
from typing import TypeVar, overload
from typing_extensions import TypeAlias

_AnyCallable: TypeAlias = Callable[..., object]
_Func = TypeVar("_Func", bound=_AnyCallable)

# We (ab)use this decorator to also copy the signature of the source function (overload 1)
# The advantages are:
# - avoid copying all parameters and types manually while conserving type safety
# - signature properly handeled in IDEs (at least with Pylance)
# - docstring from the original function properly displayed (at least with Pylance)
# Using the other overloads returns the signature of the decorated function instead
@overload
def doc(func: _Func, /) -> Callable[[_AnyCallable], _Func]: ...
@overload
def doc(docstring: str, /, *docstrings: str | _AnyCallable, **params: object) -> Callable[[_Func], _Func]: ...
@overload
def doc(
docstring1: str | _AnyCallable, docstring2: str | _AnyCallable, /, *docstrings: str | _AnyCallable, **params: object
) -> Callable[[_Func], _Func]: ...
22 changes: 22 additions & 0 deletions stubs/geopandas-stubs/_exports.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Type checking-only module to export public symbols with a different name in __init__.pyi

import numpy as np
import pandas as pd

import geopandas as gpd
from geopandas.io.arrow import _read_feather as read_feather, _read_parquet as read_parquet
from geopandas.io.file import _list_layers as list_layers, _read_file as read_file
from geopandas.io.sql import _read_postgis as read_postgis

__all__ = [
# IO functions
"read_file",
"read_feather",
"read_parquet",
"read_postgis",
"list_layers",
# Modules for interactive use
"np",
"pd",
"gpd",
]
15 changes: 9 additions & 6 deletions stubs/geopandas-stubs/array.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import pandas as pd
from numpy.typing import ArrayLike, DTypeLike, NDArray
from pandas.api.extensions import ExtensionArray, ExtensionDtype
from pyproj import CRS, Transformer
from shapely import Geometry, Point
from shapely import Geometry
from shapely.geometry.base import BaseGeometry

from geopandas.base import _ConvertibleToCRS
from geopandas.base import _AffinityOrigin, _ConvertibleToCRS
from geopandas.sindex import SpatialIndex

_ArrayOrGeom: TypeAlias = GeometryArray | ArrayLike | Geometry
_Origin: TypeAlias = Literal["center", "centroid"] | Point | tuple[float, float] | tuple[float, float, float]

TransformerFromCRS = Transformer.from_crs

Expand Down Expand Up @@ -175,9 +174,13 @@ class GeometryArray(ExtensionArray):
def intersection_all(self) -> BaseGeometry: ...
def affine_transform(self, matrix: Incomplete) -> GeometryArray: ...
def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> GeometryArray: ...
def rotate(self, angle: float, origin: _Origin = "center", use_radians: bool = False) -> GeometryArray: ...
def scale(self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: _Origin = "center") -> GeometryArray: ...
def skew(self, xs: float = 0.0, ys: float = 0.0, origin: _Origin = "center", use_radians: bool = False) -> GeometryArray: ...
def rotate(self, angle: float, origin: _AffinityOrigin = "center", use_radians: bool = False) -> GeometryArray: ...
def scale(
self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: _AffinityOrigin = "center"
) -> GeometryArray: ...
def skew(
self, xs: float = 0.0, ys: float = 0.0, origin: _AffinityOrigin = "center", use_radians: bool = False
) -> GeometryArray: ...
def to_crs(self, crs: _ConvertibleToCRS | None = None, epsg: int | None = None) -> GeometryArray: ...
def estimate_utm_crs(self, datum_name: str = "WGS 84") -> CRS: ...
@property
Expand Down
39 changes: 33 additions & 6 deletions stubs/geopandas-stubs/base.pyi
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
from _typeshed import Incomplete, SupportsGetItem
from collections.abc import Callable, Iterable
from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence
from typing import Any, Literal, Protocol, SupportsIndex, overload, type_check_only
from typing_extensions import Self, TypeAlias, deprecated

import numpy as np
import pandas as pd
from numpy.random import BitGenerator, Generator as RandomGenerator, SeedSequence
from numpy.typing import ArrayLike, NDArray
from pandas._typing import ListLikeU
from pandas.core.base import IndexOpsMixin
from pyproj import CRS
from shapely import Geometry
from shapely import Geometry, MultiPolygon, Point, Polygon
from shapely.geometry.base import BaseGeometry

from geopandas.array import _Origin
from geopandas.array import GeometryArray
from geopandas.geodataframe import GeoDataFrame
from geopandas.geoseries import GeoSeries
from geopandas.sindex import SpatialIndex

@type_check_only
class _SupportsToWkt(Protocol):
def to_wkt(self) -> str: ...

@type_check_only
class _SupportsGeoInterface(Protocol): # noqa: PYI046
@property
def __geo_interface__(self) -> dict[str, Any]: ...

_ConvertibleToCRS: TypeAlias = str | int | tuple[str, str] | list[str] | dict[str, Any] | _SupportsToWkt
_AffinityOrigin: TypeAlias = Literal["center", "centroid"] | Point | tuple[float, float] | tuple[float, float, float]
_ClipMask: TypeAlias = GeoDataFrame | GeoSeries | Polygon | MultiPolygon | tuple[float, float, float, float] # noqa: PYI047
_BboxLike: TypeAlias = Sequence[float] | NDArray[np.floating[Any]] | Geometry | GeoDataFrame | GeoSeries # noqa: PYI047
_MaskLike: TypeAlias = dict[str, Any] | Geometry | GeoDataFrame | GeoSeries # noqa: PYI047

# XXX: cannot use IndexOpsMixin[Geometry] because of IndexOpsMixin type variable bounds
_GeoListLike: TypeAlias = ArrayLike | Sequence[Geometry] | IndexOpsMixin[Incomplete]
_ConvertibleToGeoSeries: TypeAlias = Geometry | Mapping[int, Geometry] | Mapping[str, Geometry] | _GeoListLike # noqa: PYI047

# XXX: cannot use pd.Series[Geometry] because of pd.Series type variable bounds
_GeomSeq: TypeAlias = Sequence[Geometry] | NDArray[np.object_] | pd.Series[Any] | GeometryArray | GeoSeries
_GeomCol: TypeAlias = Hashable | _GeomSeq # name of column or column values # noqa: PYI047
_ConvertibleToDataFrame: TypeAlias = ( # noqa: PYI047
ListLikeU | pd.DataFrame | dict[Any, Any] | Iterable[ListLikeU | tuple[Hashable, ListLikeU] | dict[Any, Any]]
)

def is_geometry_type(data: object) -> bool: ...

Expand Down Expand Up @@ -161,9 +184,13 @@ class GeoPandasBase:
def interpolate(self, distance: float | ArrayLike, normalized: bool = False) -> GeoSeries: ...
def affine_transform(self, matrix: Incomplete) -> GeoSeries: ...
def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> GeoSeries: ...
def rotate(self, angle: float, origin: _Origin = "center", use_radians: bool = False) -> GeoSeries: ...
def scale(self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: _Origin = "center") -> GeoSeries: ...
def skew(self, xs: float = 0.0, ys: float = 0.0, origin: _Origin = "center", use_radians: bool = False) -> GeoSeries: ...
def rotate(self, angle: float, origin: _AffinityOrigin = "center", use_radians: bool = False) -> GeoSeries: ...
def scale(
self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: _AffinityOrigin = "center"
) -> GeoSeries: ...
def skew(
self, xs: float = 0.0, ys: float = 0.0, origin: _AffinityOrigin = "center", use_radians: bool = False
) -> GeoSeries: ...
@property
def cx(self) -> SupportsGetItem[tuple[SupportsIndex | slice, SupportsIndex | slice], Self]: ...
def get_coordinates(self, include_z: bool = False, ignore_index: bool = False, index_parts: bool = False) -> pd.DataFrame: ...
Expand Down
2 changes: 1 addition & 1 deletion stubs/geopandas-stubs/explore.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def _explore(
vmax: float | None = None,
width: float | str = "100%",
height: float | str = "100%",
categories=None,
categories: Sequence[Any] | NDArray[Any] | pd.Series[Any] | pd.Index[Any] | None = None,
classification_kwds: MutableMapping[str, Any] | None = None,
control_scale: bool = True,
marker_type: str | folium.Marker | None = None,
Expand Down
47 changes: 21 additions & 26 deletions stubs/geopandas-stubs/geodataframe.pyi
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import os
from _typeshed import Incomplete, SupportsGetItem, SupportsRead, SupportsWrite
from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping, Sequence
from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping
from json import JSONEncoder
from typing import Any, Literal, Protocol, overload, type_check_only
from typing_extensions import Self, TypeAlias
from typing import Any, Literal, overload
from typing_extensions import Self

import numpy as np
import pandas as pd
from numpy.typing import ArrayLike, NDArray
from pandas._typing import AggFuncTypeFrame, AstypeArg, Axes, Axis, Dtype, GroupByObject, IndexLabel, ListLikeU, Scalar
from numpy.typing import ArrayLike
from pandas._typing import AggFuncTypeFrame, AstypeArg, Axes, Axis, Dtype, GroupByObject, IndexLabel, Scalar
from pyproj import CRS
from shapely import Geometry

from geopandas.array import GeometryArray
from geopandas.base import GeoPandasBase, _ConvertibleToCRS
from geopandas._decorator import doc
from geopandas.base import (
GeoPandasBase,
_BboxLike,
_ClipMask,
_ConvertibleToCRS,
_ConvertibleToDataFrame,
_GeomCol,
_GeomSeq,
_MaskLike,
_SupportsGeoInterface,
)
from geopandas.explore import _explore
from geopandas.geoseries import GeoSeries
from geopandas.io._geoarrow import ArrowTable, _GeomEncoding
from geopandas.io.file import _BboxLike, _MaskLike
from geopandas.io.sql import _SQLConnection
from geopandas.plotting import GeoplotAccessor
from geopandas.tools.clip import _Mask as _ClipMask

# XXX: cannot use pd.Series[Geometry] because of pd.Series type variable bounds
_GeomSeq: TypeAlias = Sequence[Geometry] | NDArray[np.object_] | pd.Series[Any] | GeometryArray | GeoSeries
_GeomCol: TypeAlias = Hashable | _GeomSeq # name of column or column values
_ConvertibleToDataFrame: TypeAlias = (
ListLikeU | pd.DataFrame | dict[Any, Any] | Iterable[ListLikeU | tuple[Hashable, ListLikeU] | dict[Any, Any]]
)

@type_check_only
class _SupportsGeoInterface(Protocol):
@property
def __geo_interface__(self) -> dict[str, Any]: ...

crs_mismatch_error: str

Expand Down Expand Up @@ -322,12 +316,13 @@ class GeoDataFrame(GeoPandasBase, pd.DataFrame): # type: ignore[misc]
) -> None: ...
@property
def plot(self) -> GeoplotAccessor: ...
explore = _explore
@doc(_explore)
def explore(self, *args, **kwargs): ... # signature of `_explore` copied in `@doc`
def sjoin(
self,
df: GeoDataFrame,
# *args, **kwargs passed to geopandas.sjoin
how: str = "inner",
how: Literal["left", "right", "inner"] = "inner",
predicate: str = "intersects",
lsuffix: str = "left",
rsuffix: str = "right",
Expand All @@ -336,7 +331,7 @@ class GeoDataFrame(GeoPandasBase, pd.DataFrame): # type: ignore[misc]
def sjoin_nearest(
self,
right: GeoDataFrame,
how: str = "inner",
how: Literal["left", "right", "inner"] = "inner",
max_distance: float | None = None,
lsuffix: str = "left",
rsuffix: str = "right",
Expand Down
21 changes: 8 additions & 13 deletions stubs/geopandas-stubs/geoseries.pyi
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import json
import os
from _typeshed import Incomplete, SupportsRead, SupportsWrite, Unused
from collections.abc import Callable, Hashable, Mapping, Sequence
from collections.abc import Callable, Hashable
from typing import Any, Literal, final, overload
from typing_extensions import Self, TypeAlias, deprecated
from typing_extensions import Self, deprecated

import pandas as pd
from numpy.typing import ArrayLike
from pandas._typing import Axes, AxisIndex, Dtype
from pandas.core.base import IndexOpsMixin
from pyproj import CRS
from shapely import Geometry
from shapely.geometry.base import BaseGeometry

from geopandas._decorator import doc
from geopandas.array import GeometryArray
from geopandas.base import GeoPandasBase, _ConvertibleToCRS
from geopandas.base import GeoPandasBase, _BboxLike, _ClipMask, _ConvertibleToCRS, _ConvertibleToGeoSeries, _MaskLike
from geopandas.explore import _explore_geoseries
from geopandas.io._geoarrow import GeoArrowArray
from geopandas.io.file import _BboxLike, _MaskLike
from geopandas.plotting import plot_series
from geopandas.tools.clip import _Mask as _ClipMask

# XXX: cannot use IndexOpsMixin[Geometry] because of IndexOpsMixin type variable bounds
_GeoListLike: TypeAlias = ArrayLike | Sequence[Geometry] | IndexOpsMixin[Incomplete]
_ConvertibleToGeoSeries: TypeAlias = Geometry | Mapping[int, Geometry] | Mapping[str, Geometry] | _GeoListLike

class GeoSeries(GeoPandasBase, pd.Series[BaseGeometry]): # type: ignore[type-var,misc] # pyright: ignore[reportInvalidTypeArguments]
# Override the weird annotation of Series.__new__ in pandas-stubs
Expand Down Expand Up @@ -163,8 +156,10 @@ class GeoSeries(GeoPandasBase, pd.Series[BaseGeometry]): # type: ignore[type-va
# *** TODO: `fillna` annotation in pandas-stubs is NOT compatible; must `-> Self` ***
# def fillna(self, value=None, method: FillnaOptions | None = None, inplace: bool = False, **kwargs): ...
def __contains__(self, other: object) -> bool: ...
plot = plot_series # type: ignore[assignment] # pyright: ignore
explore = _explore_geoseries
@doc(plot_series)
def plot(self, *args, **kwargs): ... # signature of `plot_series` copied in `@doc`
@doc(_explore_geoseries)
def explore(self, *args, **kwargs): ... # signature of `_explore_geoseries` copied in `@doc`
def explode(self, ignore_index: bool = False, index_parts: bool = False) -> GeoSeries: ...
@overload
def set_crs(
Expand Down
10 changes: 1 addition & 9 deletions stubs/geopandas-stubs/io/file.pyi
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
import os
from _typeshed import SupportsRead
from collections import OrderedDict
from collections.abc import Sequence
from typing import Any, Literal, TypedDict, overload
from typing_extensions import TypeAlias

import numpy as np
import pandas as pd
from numpy.typing import NDArray
from pandas._typing import Axes
from shapely import Geometry

from geopandas.base import _BboxLike, _MaskLike
from geopandas.geodataframe import GeoDataFrame
from geopandas.geoseries import GeoSeries

_BboxLike: TypeAlias = Sequence[float] | NDArray[np.floating[Any]] | Geometry | GeoDataFrame | GeoSeries
_MaskLike: TypeAlias = dict[str, Any] | Geometry | GeoDataFrame | GeoSeries

@overload
def _read_file(
Expand Down
7 changes: 2 additions & 5 deletions stubs/geopandas-stubs/tools/clip.pyi
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from typing import TypeVar
from typing_extensions import TypeAlias

from shapely import MultiPolygon, Polygon

from geopandas.base import _ClipMask
from geopandas.geodataframe import GeoDataFrame
from geopandas.geoseries import GeoSeries

_G = TypeVar("_G", GeoDataFrame, GeoSeries)
_Mask: TypeAlias = GeoDataFrame | GeoSeries | Polygon | MultiPolygon | tuple[float, float, float, float]

def clip(gdf: _G, mask: _Mask, keep_geom_type: bool = False, sort: bool = False) -> _G: ...
def clip(gdf: _G, mask: _ClipMask, keep_geom_type: bool = False, sort: bool = False) -> _G: ...
4 changes: 2 additions & 2 deletions stubs/geopandas-stubs/tools/geocoding.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from collections.abc import Callable, Iterable
from typing import Protocol, type_check_only

from geopandas.base import _ConvertibleToGeoSeries
from geopandas.geodataframe import GeoDataFrame
from geopandas.geoseries import GeoSeries, _ConvertibleToGeoSeries

@type_check_only
class _GeoCoder(Protocol):
Expand All @@ -14,5 +14,5 @@ class _GeoCoder(Protocol):
# below if this ever becomes a thing
def geocode(strings: Iterable[str], provider: str | Callable[..., _GeoCoder] | None = None, **kwargs) -> GeoDataFrame: ...
def reverse_geocode(
points: GeoSeries | _ConvertibleToGeoSeries, provider: str | Callable[..., _GeoCoder] | None = None, **kwargs
points: _ConvertibleToGeoSeries, provider: str | Callable[..., _GeoCoder] | None = None, **kwargs
) -> GeoDataFrame: ...
5 changes: 2 additions & 3 deletions stubtest_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ geopandas\.datasets\.*

# Inconsistent OK
geopandas\.(geodataframe\.)?GeoDataFrame\.plot
geopandas\.(geodataframe\.)?GeoDataFrame\.explore
geopandas\.(geoseries\.)?GeoSeries\.explore

# Failed to import
# Failed to import OK
geopandas\.io\._geoarrow
geopandas\._exports

# TODO Inconsistent
geopandas\.(geoseries\.)?GeoSeries\.apply
Expand Down
Loading

0 comments on commit ac9126d

Please sign in to comment.