diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d6ae3c2..33b72118 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,8 +29,13 @@ repos: - id: yamllint args: [--strict, --config-file=./tests/.yamllint.yml] + - repo: https://github.com/numpy/numpydoc + rev: v1.8.0 + hooks: + - id: numpydoc-validation + - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.9.3 hooks: - id: ruff args: [--fix] diff --git a/environments/make-env-files.py b/environments/make-env-files.py index 97f83fcd..440ac43f 100644 --- a/environments/make-env-files.py +++ b/environments/make-env-files.py @@ -38,7 +38,7 @@ def make_requirement( Parameters ---------- requirement - A requirement object + A requirement object. pin_exact If True, pin requirement to version rather than using existing specifier. Allows you to convert minimum versions to pinned versions. @@ -49,6 +49,7 @@ def make_requirement( Returns ------- requirement_str + The requirement's name and its specifier(s). """ specifiers = list(requirement.specifier) if pin_exact and len(specifiers) == 1: diff --git a/osmnx/_http.py b/osmnx/_http.py index 3cbcf70b..18c320de 100644 --- a/osmnx/_http.py +++ b/osmnx/_http.py @@ -164,6 +164,7 @@ def _get_http_headers( Returns ------- headers + The updated HTTP headers. """ if user_agent is None: user_agent = settings.http_user_agent diff --git a/osmnx/_nominatim.py b/osmnx/_nominatim.py index 926d26b6..771dba52 100644 --- a/osmnx/_nominatim.py +++ b/osmnx/_nominatim.py @@ -39,6 +39,7 @@ def _download_nominatim_element( Returns ------- response_json + The Nominatim API's response. """ # define the parameters params: OrderedDict[str, int | str] = OrderedDict() @@ -102,6 +103,7 @@ def _nominatim_request( Returns ------- response_json + The Nominatim API's response. """ if request_type not in {"search", "reverse", "lookup"}: # pragma: no cover msg = "Nominatim `request_type` must be 'search', 'reverse', or 'lookup'." diff --git a/osmnx/_overpass.py b/osmnx/_overpass.py index 0b6a7050..9d5a9c6c 100644 --- a/osmnx/_overpass.py +++ b/osmnx/_overpass.py @@ -296,6 +296,7 @@ def _create_overpass_features_query( # noqa: PLR0912 Returns ------- query + The Overpass features query. """ # create overpass settings string overpass_settings = _make_overpass_settings() @@ -450,6 +451,7 @@ def _overpass_request( Returns ------- response_json + The Overpass API's response. """ # resolve url to same IP even if there is server round-robin redirecting _http._config_dns(settings.overpass_url) diff --git a/osmnx/convert.py b/osmnx/convert.py index 0d70e22e..234ce725 100644 --- a/osmnx/convert.py +++ b/osmnx/convert.py @@ -223,7 +223,6 @@ def _validate_node_edge_gdfs( GeoDataFrame of graph nodes uniquely indexed by `osmid`. gdf_edges GeoDataFrame of graph edges uniquely multi-indexed by `(u, v, key)`. - graph_attrs Returns ------- @@ -302,6 +301,7 @@ def graph_from_gdfs( Returns ------- G + The converted MultiDiGraph. """ _validate_node_edge_gdfs(gdf_nodes, gdf_edges) @@ -354,7 +354,8 @@ def to_digraph(G: nx.MultiDiGraph, *, weight: str = "length") -> nx.DiGraph: Returns ------- - G + D + The converted DiGraph. """ # make a copy to not mutate original graph object caller passed in G = G.copy() @@ -397,6 +398,7 @@ def to_undirected(G: nx.MultiDiGraph) -> nx.MultiGraph: Returns ------- Gu + The converted MultiGraph. """ # make a copy to not mutate original graph object caller passed in G = G.copy() @@ -460,6 +462,7 @@ def _is_duplicate_edge(data1: dict[str, Any], data2: dict[str, Any]) -> bool: Returns ------- is_dupe + True if `osmid` and `geometry` are the same, otherwise False. """ is_dupe = False @@ -531,6 +534,7 @@ def _update_edge_keys(G: nx.MultiDiGraph) -> nx.MultiDiGraph: Returns ------- G + Graph with incremented keys where needed. """ # identify all the edges that are duplicates based on a sorted combination # of their origin, destination, and key. that is, edge uv will match edge vu diff --git a/osmnx/elevation.py b/osmnx/elevation.py index 6be8460d..efed1725 100644 --- a/osmnx/elevation.py +++ b/osmnx/elevation.py @@ -288,7 +288,7 @@ def add_node_elevations_google( def _elevation_request(url: str, pause: float) -> dict[str, Any]: """ - Send a HTTP GET request to a Google Maps-style Elevation API. + Send a HTTP GET request to a Google Maps-style elevation API. Parameters ---------- @@ -300,6 +300,7 @@ def _elevation_request(url: str, pause: float) -> dict[str, Any]: Returns ------- response_json + The elevation API's response. """ # check if request already exists in cache cached_response_json = _http._retrieve_from_cache(url) diff --git a/osmnx/features.py b/osmnx/features.py index 626ca08b..28d25307 100644 --- a/osmnx/features.py +++ b/osmnx/features.py @@ -351,10 +351,10 @@ def features_from_xml( ---------- filepath Path to file containing OSM XML data. - tags - Query tags to optionally filter the final GeoDataFrame. polygon Spatial boundaries to optionally filter the final GeoDataFrame. + tags + Query tags to optionally filter the final GeoDataFrame. encoding The OSM XML file's character encoding. @@ -444,6 +444,7 @@ def _process_features( Returns ------- features + The features with geometries. """ nodes = [] # all nodes, including ones that just compose ways feature_nodes = [] # nodes that possibly match our query tags @@ -534,6 +535,7 @@ def _build_way_geometry( Returns ------- geometry + The way's geometry. """ # a way is a LineString by default, but if it's a closed way and it's not # tagged area=no, check if any of its tags denote it as a polygon instead @@ -584,6 +586,7 @@ def _build_relation_geometry( Returns ------- geometry + The relation's geometry. """ inner_linestrings = [] outer_linestrings = [] @@ -641,6 +644,7 @@ def _remove_polygon_holes( Returns ------- geometry + The geometry minus inner holes. """ if len(inner_polygons) == 0: # if there are no holes to remove, geom is the union of outer polygons diff --git a/osmnx/graph.py b/osmnx/graph.py index 7a9efaee..124f5ad5 100644 --- a/osmnx/graph.py +++ b/osmnx/graph.py @@ -90,6 +90,7 @@ def graph_from_bbox( Returns ------- G + The resulting MultiDiGraph. Notes ----- @@ -180,6 +181,7 @@ def graph_from_point( Returns ------- G + The resulting MultiDiGraph. Notes ----- @@ -224,7 +226,7 @@ def graph_from_address( retain_all: bool = False, truncate_by_edge: bool = False, custom_filter: str | list[str] | None = None, -) -> nx.MultiDiGraph | tuple[nx.MultiDiGraph, tuple[float, float]]: +) -> nx.MultiDiGraph: """ Download and create a graph within some distance of an address. @@ -277,7 +279,8 @@ def graph_from_address( Returns ------- - G or (G, (lat, lon)) + G + The resulting MultiDiGraph. Notes ----- @@ -375,6 +378,7 @@ def graph_from_place( Returns ------- G + The resulting MultiDiGraph. Notes ----- @@ -456,6 +460,7 @@ def graph_from_polygon( Returns ------- G + The resulting MultiDiGraph. Notes ----- @@ -564,6 +569,7 @@ def graph_from_xml( Returns ------- G + The resulting MultiDiGraph. """ # transmogrify file of OSM XML data into JSON response_jsons = [_osm_xml._overpass_json_from_xml(Path(filepath), encoding)] @@ -599,15 +605,13 @@ def _create_graph( ---------- response_jsons Iterable of JSON responses from the Overpass API. - retain_all - If True, return the entire graph even if it is not connected. - Otherwise, retain only the largest weakly connected component. bidirectional If True, create bidirectional edges for one-way streets. Returns ------- G + The resulting MultiDiGraph. """ # each dict's keys are OSM IDs and values are dicts of attributes nodes: dict[int, dict[str, Any]] = {} @@ -672,6 +676,7 @@ def _convert_node(element: dict[str, Any]) -> dict[str, Any]: Returns ------- node + The converted node. """ node = {"y": element["lat"], "x": element["lon"]} if "tags" in element: @@ -693,6 +698,7 @@ def _convert_path(element: dict[str, Any]) -> dict[str, Any]: Returns ------- path + The converted path. """ path = {"osmid": element["id"]} @@ -749,6 +755,7 @@ def _is_path_one_way(attrs: dict[str, Any], bidirectional: bool, oneway_values: Returns ------- is_one_way + True if path allows travel in only one direction, otherwise False. """ # rule 1 if settings.all_oneway: @@ -795,6 +802,7 @@ def _is_path_reversed(attrs: dict[str, Any], reversed_values: set[str]) -> bool: Returns ------- is_reversed + True if nodes' order should be reversed, otherwise False. """ return "oneway" in attrs and attrs["oneway"] in reversed_values diff --git a/osmnx/io.py b/osmnx/io.py index 45c66719..bd8ad056 100644 --- a/osmnx/io.py +++ b/osmnx/io.py @@ -179,6 +179,7 @@ def load_graphml( Returns ------- G + The loaded MultiDiGraph. """ if (filepath is None and graphml_str is None) or ( filepath is not None and graphml_str is not None @@ -310,6 +311,7 @@ def _convert_graph_attr_types(G: nx.MultiDiGraph, dtypes: dict[str, Any]) -> nx. Returns ------- G + The graph with its graph-level attributes' types converted. """ # remove node_default and edge_default metadata keys if they exist G.graph.pop("node_default", None) @@ -335,6 +337,7 @@ def _convert_node_attr_types(G: nx.MultiDiGraph, dtypes: dict[str, Any]) -> nx.M Returns ------- G + The graph with its nodes' attributes' types converted. """ for _, data in G.nodes(data=True): # first, eval stringified lists, dicts, or sets to convert them to objects @@ -365,6 +368,7 @@ def _convert_edge_attr_types(G: nx.MultiDiGraph, dtypes: dict[str, Any]) -> nx.M Returns ------- G + The graph with its edges' attributes' types converted. """ # for each edge in the graph, eval attribute value lists and convert types for _, _, data in G.edges(data=True, keys=False): @@ -411,11 +415,12 @@ def _convert_bool_string(value: bool | str) -> bool: Parameters ---------- value - The string value to convert to bool. + The string to convert to bool. Returns ------- bool_value + The boolean equivalent of the string literal. """ if isinstance(value, bool): return value diff --git a/osmnx/plot.py b/osmnx/plot.py index 9056296a..fdc92ed9 100644 --- a/osmnx/plot.py +++ b/osmnx/plot.py @@ -55,13 +55,13 @@ def get_colors( Parameters ---------- n - How many colors to generate. + How many colors to sample. cmap - Name of the matplotlib colormap from which to choose the colors. + Name of the matplotlib colormap from which to sample the colors. start - Where to start in the colorspace (from 0 to 1). + Where to start sampling from the colorspace (from 0 to 1). stop - Where to end in the colorspace (from 0 to 1). + Where to end sampling from the colorspace (from 0 to 1). alpha If `None`, return colors as HTML-like hex triplet "#rrggbb" RGB strings. If `float`, return as "#rrggbbaa" RGBa strings. @@ -69,6 +69,7 @@ def get_colors( Returns ------- color_list + The sampled colors. """ _verify_mpl() color_gen = (colormaps[cmap](x) for x in np.linspace(start, stop, n)) @@ -237,6 +238,7 @@ def plot_graph( # noqa: PLR0913 Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ _verify_mpl() max_node_size = max(node_size) if isinstance(node_size, Sequence) else node_size @@ -325,12 +327,13 @@ def plot_graph_route( Size of the origin and destination nodes. ax If not None, plot on this pre-existing axes instance. - pg_kwargs + **pg_kwargs Keyword arguments to pass to `plot_graph`. Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ _verify_mpl() if ax is None: @@ -394,12 +397,13 @@ def plot_graph_routes( route_linewidths If float, the one linewidth for all routes. Otherwise, the linewidth for each route. - pgr_kwargs + **pgr_kwargs Keyword arguments to pass to `plot_graph_route`. Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ # make iterables lists (so we're guaranteed to be able to get their sizes) routes = list(routes) @@ -487,12 +491,13 @@ def plot_figure_ground( Fallback width, in pixels, for any street type not in `street_widths`. color The color of the streets. - pg_kwargs + **pg_kwargs Keyword arguments to pass to `plot_graph`. Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ _verify_mpl() @@ -628,6 +633,7 @@ def plot_footprints( # noqa: PLR0913 Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ _verify_mpl() fig, ax = _get_fig_ax(ax=ax, figsize=figsize, bgcolor=bgcolor, polar=False) @@ -731,6 +737,7 @@ def plot_orientation( # noqa: PLR0913 Returns ------- fig, ax + The resulting matplotlib figure and polar axes objects. """ _verify_mpl() @@ -903,6 +910,7 @@ def _save_and_show( Returns ------- fig, ax + The matplotlib figure and axes objects. """ fig.canvas.draw() fig.canvas.flush_events() @@ -964,6 +972,7 @@ def _config_ax(ax: Axes, crs: Any, bbox: tuple[float, float, float, float], padd Returns ------- ax + The configured matplotlib axes object. """ # set the axes view limits to bbox + relative padding left, bottom, right, top = bbox @@ -1035,6 +1044,7 @@ def _get_fig_ax( Returns ------- fig, ax + The resulting matplotlib figure and axes objects. """ if ax is None: if polar: diff --git a/osmnx/projection.py b/osmnx/projection.py index 2e946bd0..4d143c5f 100644 --- a/osmnx/projection.py +++ b/osmnx/projection.py @@ -31,7 +31,7 @@ def is_projected(crs: Any) -> bool: # noqa: ANN401 Returns ------- projected - True if `crs` is projected, otherwise False + True if `crs` is projected, otherwise False. """ return bool(gpd.GeoSeries(crs=crs).crs.is_projected) diff --git a/osmnx/routing.py b/osmnx/routing.py index 013e17b1..68ae581d 100644 --- a/osmnx/routing.py +++ b/osmnx/routing.py @@ -198,6 +198,7 @@ def route_to_gdf( Returns ------- gdf_edges + The ordered edges in the path. """ pairs = zip(route[:-1], route[1:]) uvk = ((u, v, min(G[u][v].items(), key=lambda i: i[1][weight])[0]) for u, v in pairs) @@ -316,7 +317,7 @@ def shortest_path( Parameters ---------- G - Input graph, + Input graph. orig Origin node ID(s). dest diff --git a/osmnx/simplification.py b/osmnx/simplification.py index 7de4d062..ca259522 100644 --- a/osmnx/simplification.py +++ b/osmnx/simplification.py @@ -55,7 +55,7 @@ def _is_endpoint( G Input graph. node - The ID of the node. + The ID of the node to check. node_attrs_include Node attribute names for relaxing the strictness of endpoint determination. A node is always an endpoint if it possesses one or @@ -69,6 +69,7 @@ def _is_endpoint( Returns ------- endpoint + True if node is an endpoint, otherwise False. """ neighbors = set(list(G.predecessors(node)) + list(G.successors(node))) n = len(neighbors) diff --git a/osmnx/utils.py b/osmnx/utils.py index 2e4a5a8f..4d7dfa4f 100644 --- a/osmnx/utils.py +++ b/osmnx/utils.py @@ -73,6 +73,7 @@ def ts(style: str = "datetime", template: str | None = None) -> str: Returns ------- timestamp + The current timestamp. """ if template is None: if style == "datetime": @@ -174,6 +175,7 @@ def _get_logger(name: str, filename: str) -> lg.Logger: Returns ------- logger + The logger. """ logger = lg.getLogger(name) diff --git a/osmnx/utils_geo.py b/osmnx/utils_geo.py index f3fb8fc2..8fd13147 100644 --- a/osmnx/utils_geo.py +++ b/osmnx/utils_geo.py @@ -142,6 +142,7 @@ def _consolidate_subdivide_geometry(geom: Polygon | MultiPolygon) -> MultiPolygo Returns ------- geom + The resulting consolidated and subdivided geometry. """ if not isinstance(geom, (Polygon, MultiPolygon)): # pragma: no cover msg = "Geometry must be a shapely Polygon or MultiPolygon." @@ -190,6 +191,7 @@ def _quadrat_cut_geometry(geom: Polygon | MultiPolygon, quadrat_width: float) -> Returns ------- geom + The resulting split-up geometry. """ # min number of dividing lines (3 produces a grid of 4 quadrat squares) min_num = 3 @@ -429,6 +431,7 @@ def bbox_to_poly(bbox: tuple[float, float, float, float]) -> Polygon: Returns ------- polygon + The resulting bounding box polygon. """ left, bottom, right, top = bbox return Polygon([(left, bottom), (right, bottom), (right, top), (left, top)]) diff --git a/pyproject.toml b/pyproject.toml index 92742ae2..909c7905 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,9 @@ strict = true warn_no_return = true warn_unreachable = true +[tool.numpydoc_validation] +checks = ["all", "ES01", "EX01", "GL08", "PR04", "RT03", "SA01"] + [tool.ruff] cache-dir = "~/.cache/pre-commit/ruff" exclude = ["build/*"]