Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expand ruff rules #73

Merged
merged 11 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
# publish:
# runs-on: ubuntu-latest
# needs: test
# # The following steps to build a Docker image and publish to the GitHub container registry on release. Alternatively, can replace with other publising steps (ie. publishing to PyPI, deploying documentation etc.)
# # The following steps to build a Docker image and publish to the GitHub container registry on release. Alternatively, can replace with other publishing steps (ie. publishing to PyPI, deploying documentation etc.)
# steps:
# - name: Login to GitHub Container Registry
# uses: docker/login-action@v3
Expand Down
22 changes: 22 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,25 @@ repos:
hooks:
- id: markdownlint
args: ["--disable", "MD013", "--"]

- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
- id: codespell
name: Check common misspellings in text files with codespell.
additional_dependencies:
- tomli

- repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.7.0
hooks:
- id: pyproject-fmt
name: Apply a consistent format to pyproject.toml

- repo: https://github.com/dosisod/refurb
rev: v1.28.0
hooks:
- id: refurb
name: Modernizing Python codebases using Refurb
additional_dependencies:
- numpy
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ To use this repository as a template for your own application:

To add or remove dependencies:

1. Edit the `dependencies` variables in the `pyproject.toml` file (aim to keep develpment tools separate from the project requirements).
1. Edit the `dependencies` variables in the `pyproject.toml` file (aim to keep development tools separate from the project requirements).
2. Update the requirements files:
- `pip-compile` for `requirements.txt` - the project requirements.
- `pip-compile --extra dev -o dev-requirements.txt` for `dev-requirements.txt` - the development requirements.
Expand Down
90 changes: 53 additions & 37 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
requires = [
"setuptools",
"setuptools-scm",
]

[tool.setuptools.packages.find]
exclude = ["htmlcov"] # Exclude the coverage report file from setuptools package finder
Expand All @@ -13,44 +16,50 @@ authors = [
{ name = "Imperial College London RSE Team", email = "ict-rse-team@imperial.ac.uk" }
]
requires-python = ">=3.10"
dependencies = [ # TODO definitely don't need all of these
"cdsapi",
"fastparquet",
"fiona",
"geopandas",
"geopy",
"GitPython",
"matplotlib",
"netcdf4",
"networkx",
"numpy",
"osmnx",
"pandas",
"pyarrow",
"pydantic",
"pysheds",
"pyswmm",
"PyYAML",
"rasterio",
"rioxarray",
"SALib",
"SciPy",
"shapely",
"snkit",
"tqdm",
"xarray"
]

classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
# TODO definitely don't need all of these
"cdsapi",
"fastparquet",
"fiona",
"geopandas",
"geopy",
"GitPython",
"matplotlib",
"netcdf4",
"networkx",
"numpy",
"osmnx",
"pandas",
"pyarrow",
"pydantic",
"pysheds",
"pyswmm",
"PyYAML",
"rasterio",
"rioxarray",
"SALib",
"SciPy",
"shapely",
"snkit",
"tqdm",
"xarray",
]
[project.optional-dependencies]
dev = [
"ruff",
"mypy",
"pip-tools",
"pre-commit",
"pytest",
"pytest-cov",
"pytest-mypy",
"pytest-mock"
"mypy",
"pip-tools",
"pre-commit",
"pytest",
"pytest-cov",
"pytest-mock",
"pytest-mypy",
"ruff",
]

[tool.mypy]
Expand All @@ -76,3 +85,10 @@ select = ["D", "E", "F", "I"] # pydocstyle, pycodestyle, Pyflakes, isort

[tool.ruff.pydocstyle]
convention = "google"

[tool.codespell]
skip = "swmmanywhere/defs/iso_converter.yml,*.inp"
ignore-words-list = "gage,gages"

[tool.refurb]
ignore = [184]
42 changes: 26 additions & 16 deletions swmmanywhere/geospatial_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

@author: Barnaby Dobson
"""
import itertools
import json
import math
import operator
from copy import deepcopy
from functools import lru_cache
from pathlib import Path
Expand Down Expand Up @@ -248,8 +250,9 @@ def reproject_graph(G: nx.Graph,
# Convert and add edges with 'geometry' property
for u, v, data in G_new.edges(data=True):
if 'geometry' in data.keys():
data['geometry'] = sgeom.LineString(transformer.transform(x, y)
for x, y in data['geometry'].coords)
data['geometry'] = sgeom.LineString(
itertools.starmap(transformer.transform,
data['geometry'].coords))
else:
data['geometry'] = sgeom.LineString([[G_new.nodes[u]['x'],
G_new.nodes[u]['y']],
Expand Down Expand Up @@ -440,7 +443,7 @@ def delineate_catchment(grid: pysheds.sgrid.sGrid,
polys.append({'id': id,
'geometry': catchment_polygon,
'area': catchment_polygon.area})
polys = sorted(polys, key=lambda d: d['area'], reverse=True)
polys.sort(key=operator.itemgetter("area"), reverse=True)
return gpd.GeoDataFrame(polys, crs = grid.crs)

def remove_intersections(polys: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
Expand All @@ -461,9 +464,11 @@ def remove_intersections(polys: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
result_polygons = polys.copy()

# Sort the polygons by area (smallest first)
result_polygons['area'] = result_polygons.geometry.area
result_polygons = result_polygons.sort_values('area', ascending=True)
result_polygons = result_polygons.reset_index(drop=True)
result_polygons = (
result_polygons.assign(area=result_polygons.geometry.area)
.sort_values('area', ascending=True)
.reset_index(drop=True)
)

# Store the area of 'trimmed' polygons into a combined geometry, starting
# with the smallest area polygon
Expand All @@ -480,8 +485,10 @@ def remove_intersections(polys: gpd.GeoDataFrame) -> gpd.GeoDataFrame:

# Sort the polygons by area (largest first) - this is just to conform to
# the unit test and is not strictly essential
result_polygons = result_polygons.sort_values('area', ascending=False)
result_polygons = result_polygons.drop('area', axis=1)
result_polygons = (
result_polygons.sort_values('area', ascending=False)
.drop('area', axis=1)
)

return result_polygons

Expand Down Expand Up @@ -592,7 +599,7 @@ def derive_subcatchments(G: nx.Graph, fid: Path) -> gpd.GeoDataFrame:
polys_gdf = polys_gdf[~polys_gdf['geometry'].is_empty]

# Remove zero area subareas and attach to nearest polygon
removed_subareas: List[sgeom.Polygon] = list() # needed for mypy
removed_subareas: List[sgeom.Polygon] = [] # needed for mypy
def remove_(mp): return remove_zero_area_subareas(mp, removed_subareas)
polys_gdf['geometry'] = polys_gdf['geometry'].apply(remove_)
polys_gdf = attach_unconnected_subareas(polys_gdf, removed_subareas)
Expand All @@ -614,7 +621,7 @@ def derive_rc(polys_gdf: gpd.GeoDataFrame,
impervious area is calculated by overlaying the subcatchments with building
footprints and all edges in G buffered by their width parameter (i.e., to
calculate road area).

Args:
polys_gdf (gpd.GeoDataFrame): A GeoDataFrame containing polygons that
represent subcatchments with columns: 'geometry', 'area', and 'id'.
Expand All @@ -632,12 +639,15 @@ def derive_rc(polys_gdf: gpd.GeoDataFrame,
## Format as swmm type catchments

# TODO think harder about lane widths (am I double counting here?)
lines = []
for u, v, x in G.edges(data=True):
lines.append({'geometry' : x['geometry'].buffer(x['width'],
cap_style = 2,
join_style=2),
'id' : x['id']})
lines = [
{
'geometry': x['geometry'].buffer(x['width'],
cap_style=2,
join_style=2),
'id': x['id']
}
for u, v, x in G.edges(data=True)
]
lines_df = pd.DataFrame(lines)
lines_gdf = gpd.GeoDataFrame(lines_df,
geometry=lines_df.geometry,
Expand Down
39 changes: 16 additions & 23 deletions swmmanywhere/graph_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ def load_graph(fid: Path) -> nx.Graph:
def _serialize_line_string(obj):
if isinstance(obj, shapely.LineString):
return obj.wkt
else:
return obj
return obj
def save_graph(G: nx.Graph,
fid: Path) -> None:
"""Save a graph to a file.
Expand All @@ -57,7 +56,7 @@ def save_graph(G: nx.Graph,
"""
json_data = nx.node_link_data(G)

with open(fid, 'w') as file:
with fid.open('w') as file:
json.dump(json_data,
file,
default = _serialize_line_string)
Expand All @@ -73,25 +72,21 @@ class BaseGraphFunction(ABC):
their requirements and additions a-priori when the list is provided.
"""

required_edge_attributes: List[str] = list()
adds_edge_attributes: List[str] = list()
required_node_attributes: List[str] = list()
adds_node_attributes: List[str] = list()
required_edge_attributes: List[str] = []
adds_edge_attributes: List[str] = []
required_node_attributes: List[str] = []
adds_node_attributes: List[str] = []
def __init_subclass__(cls,
required_edge_attributes: Optional[List[str]] = None,
adds_edge_attributes: Optional[List[str]] = None,
required_node_attributes : Optional[List[str]] = None,
adds_node_attributes : Optional[List[str]] = None
):
"""Set the required and added attributes for the subclass."""
cls.required_edge_attributes = required_edge_attributes if \
required_edge_attributes else []
cls.adds_edge_attributes = adds_edge_attributes if \
adds_edge_attributes else []
cls.required_node_attributes = required_node_attributes if \
required_node_attributes else []
cls.adds_node_attributes = adds_node_attributes if \
adds_node_attributes else []
cls.required_edge_attributes = required_edge_attributes or []
cls.adds_edge_attributes = adds_edge_attributes or []
cls.required_node_attributes = required_node_attributes or []
cls.adds_node_attributes = adds_node_attributes or []

@abstractmethod
def __call__(self,
Expand Down Expand Up @@ -243,7 +238,7 @@ def __call__(self, G: nx.Graph, **kwargs) -> nx.Graph:
Returns:
G (nx.Graph): A graph
"""
#TODO the geometry is left as is currently - should be reveresed, however
#TODO the geometry is left as is currently - should be reversed, however
# in original osmnx geometry there are some incorrectly directed ones
# someone with more patience might check start and end Points to check
# which direction the line should be going in...
Expand Down Expand Up @@ -734,8 +729,7 @@ def __call__(self, G: nx.Graph,
if d['edge_type'] == 'outlet':
nodes_to_remove.append(v)
else:
nodes_to_remove.append(u)
nodes_to_remove.append(v)
nodes_to_remove.extend((u,v))

isolated_nodes = list(nx.isolates(G))

Expand Down Expand Up @@ -779,7 +773,7 @@ def __call__(self, G: nx.Graph,
# Push the neighbor to the heap
heappush(heap, (alt_dist, neighbor))

edges_to_keep = set()
edges_to_keep: set = set() # no clue why mypy wants me to do this..
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe doing this will satisfy mypy: edges_to_keep: set[tuple[int, int]] = {}

for path in paths.values():
# Assign outlet
outlet = path[0]
Expand All @@ -788,10 +782,9 @@ def __call__(self, G: nx.Graph,
G.nodes[node]['shortest_path'] = shortest_paths[node]

# Store path
for i in range(len(path) - 1):
edges_to_keep.add((path[i+1], path[i]))

# Remvoe edges not on paths
edges_to_keep.update(zip(path[1:], path[:-1]))

# Remove edges not on paths
new_graph = G.copy()
for u,v in G.edges():
if (u,v) not in edges_to_keep:
Expand Down
8 changes: 4 additions & 4 deletions swmmanywhere/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ def _generate_property(self,
"""
if property_name in self.__dict__.keys():
return self.__dict__[property_name]
else:
return self._generate_path(self.project_name,
getattr(self, location),
property_name)

return self._generate_path(self.project_name,
getattr(self, location),
property_name)

def _generate_project(self):
return self._generate_path(self.project_name)
Expand Down
Loading