From ccac7cfc25eb825f5d58ead1029d8d2dd00b69a7 Mon Sep 17 00:00:00 2001 From: Lee Zeman Date: Tue, 17 Jun 2025 17:17:20 -0700 Subject: [PATCH 1/3] add black --- ce/api/__init__.py | 11 +- ce/api/data.py | 7 +- ce/api/geo.py | 2 +- ce/api/grid.py | 3 +- ce/api/health/regions.py | 7 +- ce/api/lister.py | 4 +- ce/api/metadata.py | 12 ++- ce/api/models.py | 4 +- ce/api/multimeta.py | 5 +- ce/api/multistats.py | 3 +- ce/api/stats.py | 12 ++- ce/api/streamflow/downstream.py | 22 ++-- ce/api/streamflow/shared.py | 2 +- ce/api/streamflow/watershed.py | 32 +++--- ce/api/streamflow/watershed_streams.py | 18 ++-- ce/api/timeseries.py | 3 +- .../test_build_downstream_watershed.py | 101 ++++++++++++++++-- .../build_watershed/test_build_watershed.py | 37 ++++++- .../test_build_watershed_streams.py | 72 +++++++++++-- ce/tests/api/streamflow/watershed/conftest.py | 32 +++++- .../watershed/test_downstream_watershed.py | 10 +- .../streamflow/watershed/test_watershed.py | 15 ++- .../watershed/test_watershed_streams.py | 15 ++- ce/tests/geo_data_grid_2d/vic/test_vic.py | 43 +++++++- ce/tests/helpers/test_utils.py | 2 +- ce/tests/test_util.py | 2 +- ce/views.py | 2 +- 27 files changed, 370 insertions(+), 108 deletions(-) diff --git a/ce/api/__init__.py b/ce/api/__init__.py index ec61a04c..09a0cf2b 100644 --- a/ce/api/__init__.py +++ b/ce/api/__init__.py @@ -1,10 +1,9 @@ -""" PCIC Climate Explorer backend API module +"""PCIC Climate Explorer backend API module .. moduleauthor:: James Hiebert """ - import inspect from datetime import datetime @@ -169,8 +168,10 @@ def format_dates(obj): if not isinstance(obj, dict): return obj return { - key: val.strftime(time_format) - if isinstance(val, datetime) - else format_dates(val) + key: ( + val.strftime(time_format) + if isinstance(val, datetime) + else format_dates(val) + ) for key, val in obj.items() } diff --git a/ce/api/data.py b/ce/api/data.py index 39833781..e32c9db0 100644 --- a/ce/api/data.py +++ b/ce/api/data.py @@ -1,5 +1,4 @@ -"""module for requesting data across multiple files through the API -""" +"""module for requesting data across multiple files through the API""" import numpy as np import os @@ -61,9 +60,9 @@ def data( climatological_statistic (str): Statistical operation applied to variable in a climatological dataset (e.g "mean", "standard_deviation", "percentile). Defaulted to "mean". - + percentile (float): if climatological_statistic is "percentile", specifies what - percentile value to use. A percentile value must be provided if the + percentile value to use. A percentile value must be provided if the climatological_statistic is "percentile". is_thredds (bool): If set to `True` the filepath will be searched for diff --git a/ce/api/geo.py b/ce/api/geo.py index d2af0251..d15cec3f 100644 --- a/ce/api/geo.py +++ b/ce/api/geo.py @@ -219,7 +219,7 @@ def polygon_to_mask(nc, resource, poly, variable): def make_masked_file_key(nc, resource, wkt, varname): """generates a key suitable for characterizing a masked netCDF file: - filename and polygon""" + filename and polygon""" return (resource, wkt) diff --git a/ce/api/grid.py b/ce/api/grid.py index 871a64c1..e5ea245c 100644 --- a/ce/api/grid.py +++ b/ce/api/grid.py @@ -1,5 +1,4 @@ -"""module for requesting the lat/lon grid for a given model run file -""" +"""module for requesting the lat/lon grid for a given model run file""" from sqlalchemy.orm.exc import NoResultFound diff --git a/ce/api/health/regions.py b/ce/api/health/regions.py index 5eaeff08..90ef8c59 100644 --- a/ce/api/health/regions.py +++ b/ce/api/health/regions.py @@ -1,4 +1,5 @@ """module for requesting a summary of stored region data status.""" + import os from csv import DictReader from ce.api.multimeta import multimeta @@ -8,9 +9,9 @@ def region_status(region, metadata): """Opens a stored data file for a region, checks the modtime of the - stored data against the current versions of each file (as listed - in the metadata) and returns a list of the each file from which - stored data was calculated and its status.""" + stored data against the current versions of each file (as listed + in the metadata) and returns a list of the each file from which + stored data was calculated and its status.""" date_format = "%Y-%m-%dT%H:%M:%SZ" diff --git a/ce/api/lister.py b/ce/api/lister.py index 1979ab8f..2f6d5563 100644 --- a/ce/api/lister.py +++ b/ce/api/lister.py @@ -1,11 +1,9 @@ -"""module for requesting unique_ids from ensemble or model short name -""" +"""module for requesting unique_ids from ensemble or model short name""" from modelmeta import Ensemble def lister(sesh, ensemble_name="ce_files", model=None): - """ Args sesh (sqlalchemy.orm.session.Session): A database Session object diff --git a/ce/api/metadata.py b/ce/api/metadata.py index faeb4ebf..b4ce7dd8 100644 --- a/ce/api/metadata.py +++ b/ce/api/metadata.py @@ -1,5 +1,5 @@ -"""module for requesting metadata for one single file through the API -""" +"""module for requesting metadata for one single file through the API""" + from sqlalchemy.orm.exc import NoResultFound from modelmeta import DataFile @@ -131,4 +131,10 @@ def metadata(sesh, model_id, extras=""): } ) - return {model_id: {**base_values, **requested_extra_values, **time_values,}} + return { + model_id: { + **base_values, + **requested_extra_values, + **time_values, + } + } diff --git a/ce/api/models.py b/ce/api/models.py index fde952ca..f2cf82e0 100644 --- a/ce/api/models.py +++ b/ce/api/models.py @@ -1,11 +1,9 @@ -"""module for requesting list of available models -""" +"""module for requesting list of available models""" from modelmeta import Ensemble def models(sesh, ensemble_name="ce_files"): - """ Args sesh (sqlalchemy.orm.session.Session): A database Session object diff --git a/ce/api/multimeta.py b/ce/api/multimeta.py index ae3dfbc5..742962b3 100644 --- a/ce/api/multimeta.py +++ b/ce/api/multimeta.py @@ -1,5 +1,4 @@ -"""module for requesting metadata from multiple files based on model or ensemble -""" +"""module for requesting metadata from multiple files based on model or ensemble""" from modelmeta import DataFile, Model, Emission, Run from modelmeta import DataFileVariableGridded, VariableAlias, TimeSet @@ -47,7 +46,7 @@ def multimeta( climatological_statistic(str): Statistical operation applied to variable in a climatological dataset (e.g "mean", "standard_deviation", "percentile"). Defaulted to "mean". - + percentile(float): optionally, specify a a single percentile values to filter on. Returns: diff --git a/ce/api/multistats.py b/ce/api/multistats.py index ae08fbf6..560feabb 100644 --- a/ce/api/multistats.py +++ b/ce/api/multistats.py @@ -1,5 +1,4 @@ -"""module for requesting stats from multiple files based on model or ensemble -""" +"""module for requesting stats from multiple files based on model or ensemble""" from ce.api.stats import stats from ce.api.util import search_for_unique_ids diff --git a/ce/api/stats.py b/ce/api/stats.py index 76c72df1..e9fa945e 100644 --- a/ce/api/stats.py +++ b/ce/api/stats.py @@ -1,5 +1,4 @@ -"""module for requsting summary statistics, averaged across a region -""" +"""module for requsting summary statistics, averaged across a region""" import numpy as np import numpy.ma as ma @@ -26,7 +25,12 @@ def stats( - sesh, id_, time, area, variable, is_thredds=False, + sesh, + id_, + time, + area, + variable, + is_thredds=False, ): """Request and calculate summary statistics averaged across a region @@ -133,7 +137,7 @@ def stats( def array_stats(array): """Return the min, max, mean, median, standard deviation and number - of cells of a 3d data grid (numpy.ma.MaskedArray) + of cells of a 3d data grid (numpy.ma.MaskedArray) """ return { "min": np.min(array).item(), diff --git a/ce/api/streamflow/downstream.py b/ce/api/streamflow/downstream.py index a6a7ded0..a00be7aa 100644 --- a/ce/api/streamflow/downstream.py +++ b/ce/api/streamflow/downstream.py @@ -14,6 +14,7 @@ spatial tuple to a data index tuple and vice versa, also switch the dimension order accordingly. """ + from gettext import dpgettext from this import d from contexttimer import Timer @@ -53,7 +54,7 @@ def downstream(sesh, station, ensemble_name): and converting their contents to `VicDataGrid` objects for consumption by `downstream_worker`, which as its name suggests, does most of the work. """ - + station_lonlat = setup(station) with get_time_invariant_variable_dataset( @@ -75,7 +76,8 @@ def downstream(sesh, station, ensemble_name): def downstream_worker( - station_lonlat, flow_direction, + station_lonlat, + flow_direction, ): """Compute the watershed. @@ -136,16 +138,16 @@ def build_downstream_watershed(target, routing, direction_map, debug=False): represented by an offset of +1 or -1, respectively. :param debug: Boolean indicating whether this function should compute and return debug information. - :return: Tuple of cells (cell indices as tuples) that drain from `target` in + :return: Tuple of cells (cell indices as tuples) that drain from `target` in a downstream flow order. Notes: - In this function, a cell is represented by an (x, y) index pair. - - Routing graphs can and in practice do contain cycles. Variable - `stream` is used to check whether a cell has already been - visited during the traversal of the routing graph, i.e., whether we + - Routing graphs can and in practice do contain cycles. Variable + `stream` is used to check whether a cell has already been + visited during the traversal of the routing graph, i.e., whether we are cycling, and if so not to repeat that subgraph. """ @@ -156,10 +158,12 @@ def downstream(stream): cell_routing = routing[stream[-1]] downstream_neighbour = vec_add(stream[-1], direction_map[int(cell_routing)]) - if downstream_neighbour in stream or not is_valid_index(downstream_neighbour, routing.shape): + if downstream_neighbour in stream or not is_valid_index( + downstream_neighbour, routing.shape + ): return stream stream += (downstream_neighbour,) return downstream(stream) - - return downstream((target,)) \ No newline at end of file + + return downstream((target,)) diff --git a/ce/api/streamflow/shared.py b/ce/api/streamflow/shared.py index 17ea3aec..241a44ef 100644 --- a/ce/api/streamflow/shared.py +++ b/ce/api/streamflow/shared.py @@ -55,7 +55,7 @@ def is_downstream(neighbour, cell, routing, direction_map): def VIC_direction_matrix(lat_step, lon_step): - """ Return a VIC direction matrix, which is a matrix indexed by the VIC + """Return a VIC direction matrix, which is a matrix indexed by the VIC streamflow direction codes 0...9, with the value at index `i` indicating the offsets from the data index in a streamflow file required to step in that streamflow direction. diff --git a/ce/api/streamflow/watershed.py b/ce/api/streamflow/watershed.py index 08a0fc78..cc51b7df 100644 --- a/ce/api/streamflow/watershed.py +++ b/ce/api/streamflow/watershed.py @@ -14,6 +14,7 @@ spatial tuple to a data index tuple and vice versa, also switch the dimension order accordingly. """ + import math from contexttimer import Timer @@ -59,17 +60,21 @@ def watershed(sesh, station, ensemble_name): """ station_lonlat = setup(station) - with get_time_invariant_variable_dataset( - sesh, ensemble_name, "flow_direction" - ) as flow_direction_ds, get_time_invariant_variable_dataset( - sesh, ensemble_name, "elev" - ) as elevation_ds, get_time_invariant_variable_dataset( - sesh, ensemble_name, "elevmin" - ) as elevation_min_ds, get_time_invariant_variable_dataset( - sesh, ensemble_name, "elevmax" - ) as elevation_max_ds, get_time_invariant_variable_dataset( - sesh, ensemble_name, "area" - ) as area_ds: + with ( + get_time_invariant_variable_dataset( + sesh, ensemble_name, "flow_direction" + ) as flow_direction_ds, + get_time_invariant_variable_dataset( + sesh, ensemble_name, "elev" + ) as elevation_ds, + get_time_invariant_variable_dataset( + sesh, ensemble_name, "elevmin" + ) as elevation_min_ds, + get_time_invariant_variable_dataset( + sesh, ensemble_name, "elevmax" + ) as elevation_max_ds, + get_time_invariant_variable_dataset(sesh, ensemble_name, "area") as area_ds, + ): try: return worker( station_lonlat, @@ -218,7 +223,10 @@ def worker( "elevation_units": elevation_mean.units, "area_units": area.units, }, - "melton_ratio": {"units": "km/km", "value": m_ratio,}, + "melton_ratio": { + "units": "km/km", + "value": m_ratio, + }, "boundary": geojson_feature( outline, properties={ diff --git a/ce/api/streamflow/watershed_streams.py b/ce/api/streamflow/watershed_streams.py index 1ceda591..9bb3452c 100644 --- a/ce/api/streamflow/watershed_streams.py +++ b/ce/api/streamflow/watershed_streams.py @@ -14,6 +14,7 @@ spatial tuple to a data index tuple and vice versa, also switch the dimension order accordingly. """ + from contexttimer import Timer from flask import abort @@ -69,8 +70,8 @@ def watershed_streams(sesh, station, ensemble_name): def worker(station_lonlat, flow_direction): - """Returns the same as watershed_streams, this function exists to make - these computations more easily testable. (Specifically, data *files* are not + """Returns the same as watershed_streams, this function exists to make + these computations more easily testable. (Specifically, data *files* are not required, only the relevant contents of those files passed as `VicDataGrid` objects. `VicDataGrid`s are much easier to construct for tests.) @@ -96,15 +97,20 @@ def worker(station_lonlat, flow_direction): # `watershed_lonlats` must be an ordered collection (not sets) because # a multi line string is an array (python list) of linestrings watershed_lonlats = [ - [flow_direction.xy_to_lonlat(xy) for xy in stream] - for stream in watershed_xys + [flow_direction.xy_to_lonlat(xy) for xy in stream] for stream in watershed_xys ] lines = MultiLineString(watershed_lonlats) return { - "streams": geojson_feature(lines,), - "debug/test": {"watershed_streams": {"time": watershed_time.elapsed,}}, + "streams": geojson_feature( + lines, + ), + "debug/test": { + "watershed_streams": { + "time": watershed_time.elapsed, + } + }, } diff --git a/ce/api/timeseries.py b/ce/api/timeseries.py index 43e0381e..910bf9f6 100644 --- a/ce/api/timeseries.py +++ b/ce/api/timeseries.py @@ -1,5 +1,4 @@ -"""module for requesting sinlge-file data through the API -""" +"""module for requesting sinlge-file data through the API""" import numpy as np from sqlalchemy.orm.exc import NoResultFound diff --git a/ce/tests/api/streamflow/watershed/build_watershed/test_build_downstream_watershed.py b/ce/tests/api/streamflow/watershed/build_watershed/test_build_downstream_watershed.py index 3b65f91f..3747f884 100644 --- a/ce/tests/api/streamflow/watershed/build_watershed/test_build_downstream_watershed.py +++ b/ce/tests/api/streamflow/watershed/build_watershed/test_build_downstream_watershed.py @@ -8,17 +8,34 @@ # Fully connected routing arrays: All cells connect to the mouth -routing_fc_3x3 = np_array(((S, S, SW), (S, SW, W), (OUTLET, W, W),)) +routing_fc_3x3 = np_array( + ( + (S, S, SW), + (S, SW, W), + (OUTLET, W, W), + ) +) # Linear ccw spiral; distal point at (1,2) routing_fc_4x4 = np_array( - ((S, W, W, W), (S, S, W, N), (S, S, N, N), (OUTLET, E, E, N),) + ( + (S, W, W, W), + (S, S, W, N), + (S, S, N, N), + (OUTLET, E, E, N), + ) ) # Partially connected routing arrays: Not all cells connect to the mouth # Norteasternmost cell does not connect to mouth -routing_pc_3x3 = np_array(((S, S, N), (S, SW, W), (OUTLET, W, W),)) +routing_pc_3x3 = np_array( + ( + (S, S, N), + (S, SW, W), + (OUTLET, W, W), + ) +) # Routing arrays with loops @@ -27,13 +44,24 @@ routing_loop_1x2 = np_array(((E, W),)) # Loop covering all cells in 2x2 -routing_loop_2x2_quad = np_array(((E, S), (N, W),)) +routing_loop_2x2_quad = np_array( + ( + (E, S), + (N, W), + ) +) # Loop covering only 3 cells in 2x2 -routing_loop_2x2_tri = np_array(((E, S), (S, NW),)) +routing_loop_2x2_tri = np_array( + ( + (E, S), + (S, NW), + ) +) -@pytest.mark.parametrize("mouth, routing, direction_map, expected", +@pytest.mark.parametrize( + "mouth, routing, direction_map, expected", ( # Trivial cases ((0, 0), routing_0x0, None, ((0, 0),)), @@ -42,16 +70,69 @@ ((0, 0), routing_fc_3x3, direction_map, ((0, 0),)), ((1, 1), routing_fc_3x3, direction_map, ((1, 1), (0, 0))), ((0, 0), routing_fc_4x4, direction_map, ((0, 0),)), - ((1, 2), routing_fc_4x4, direction_map, ((1, 2), (2, 2), (2, 1), (1, 1), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (3, 0), (2, 0), (1, 0), (0, 0))), - ((2, 2), routing_fc_4x4, direction_map, ((2, 2), (2, 1), (1, 1), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (3, 0), (2, 0), (1, 0), (0, 0))), + ( + (1, 2), + routing_fc_4x4, + direction_map, + ( + (1, 2), + (2, 2), + (2, 1), + (1, 1), + (0, 1), + (0, 2), + (0, 3), + (1, 3), + (2, 3), + (3, 3), + (3, 2), + (3, 1), + (3, 0), + (2, 0), + (1, 0), + (0, 0), + ), + ), + ( + (2, 2), + routing_fc_4x4, + direction_map, + ( + (2, 2), + (2, 1), + (1, 1), + (0, 1), + (0, 2), + (0, 3), + (1, 3), + (2, 3), + (3, 3), + (3, 2), + (3, 1), + (3, 0), + (2, 0), + (1, 0), + (0, 0), + ), + ), # Partly connected watersheds ((2, 1), routing_pc_3x3, direction_map, ((2, 1), (1, 1), (0, 0))), ((2, 2), routing_pc_3x3, direction_map, ((2, 2),)), # Watersheds with loops ((0, 0), routing_loop_1x2, direction_map, ((0, 0), (0, 1))), ((0, 1), routing_loop_1x2, direction_map, ((0, 1), (0, 0))), - ((0, 0), routing_loop_2x2_quad, direction_map, ((0, 0), (1, 0), (1, 1), (0, 1))), - ((1, 1), routing_loop_2x2_quad, direction_map, ((1, 1), (0, 1), (0, 0), (1, 0))), + ( + (0, 0), + routing_loop_2x2_quad, + direction_map, + ((0, 0), (1, 0), (1, 1), (0, 1)), + ), + ( + (1, 1), + routing_loop_2x2_quad, + direction_map, + ((1, 1), (0, 1), (0, 0), (1, 0)), + ), ((0, 0), routing_loop_2x2_tri, direction_map, ((0, 0),)), ((1, 1), routing_loop_2x2_tri, direction_map, ((1, 1), (0, 1), (1, 0))), ), diff --git a/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed.py b/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed.py index c69aa4f0..4d914332 100644 --- a/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed.py +++ b/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed.py @@ -11,17 +11,34 @@ # Fully connected routing arrays: All cells connect to the mouth -routing_fc_3x3 = np_array(((S, S, SW), (S, SW, W), (OUTLET, W, W),)) +routing_fc_3x3 = np_array( + ( + (S, S, SW), + (S, SW, W), + (OUTLET, W, W), + ) +) # Linear ccw spiral; distal point at (1,2) routing_fc_4x4 = np_array( - ((S, W, W, W), (S, S, W, N), (S, S, N, N), (OUTLET, E, E, N),) + ( + (S, W, W, W), + (S, S, W, N), + (S, S, N, N), + (OUTLET, E, E, N), + ) ) # Partially connected routing arrays: Not all cells connect to the mouth # Norteasternmost cell does not connect to mouth -routing_pc_3x3 = np_array(((S, S, N), (S, SW, W), (OUTLET, W, W),)) +routing_pc_3x3 = np_array( + ( + (S, S, N), + (S, SW, W), + (OUTLET, W, W), + ) +) # Routing arrays with loops @@ -30,10 +47,20 @@ routing_loop_1x2 = np_array(((E, W),)) # Loop covering all cells in 2x2 -routing_loop_2x2_quad = np_array(((E, S), (N, W),)) +routing_loop_2x2_quad = np_array( + ( + (E, S), + (N, W), + ) +) # Loop covering only 3 cells in 2x2 -routing_loop_2x2_tri = np_array(((E, S), (S, NW),)) +routing_loop_2x2_tri = np_array( + ( + (E, S), + (S, NW), + ) +) @pytest.mark.parametrize( diff --git a/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed_streams.py b/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed_streams.py index 057661a4..a13bba78 100644 --- a/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed_streams.py +++ b/ce/tests/api/streamflow/watershed/build_watershed/test_build_watershed_streams.py @@ -11,17 +11,34 @@ # Fully connected routing arrays: All cells connect to the mouth -routing_fc_3x3 = np_array(((S, S, SW), (S, SW, W), (OUTLET, W, W),)) +routing_fc_3x3 = np_array( + ( + (S, S, SW), + (S, SW, W), + (OUTLET, W, W), + ) +) # Linear ccw spiral; distal point at (1,2) routing_fc_4x4 = np_array( - ((S, W, W, W), (S, S, W, N), (S, S, N, N), (OUTLET, E, E, N),) + ( + (S, W, W, W), + (S, S, W, N), + (S, S, N, N), + (OUTLET, E, E, N), + ) ) # Partially connected routing arrays: Not all cells connect to the mouth # Norteasternmost cell does not connect to mouth -routing_pc_3x3 = np_array(((S, S, N), (S, SW, W), (OUTLET, W, W),)) +routing_pc_3x3 = np_array( + ( + (S, S, N), + (S, SW, W), + (OUTLET, W, W), + ) +) # Routing arrays with loops @@ -30,10 +47,20 @@ routing_loop_1x2 = np_array(((E, W),)) # Loop covering all cells in 2x2 -routing_loop_2x2_quad = np_array(((E, S), (N, W),)) +routing_loop_2x2_quad = np_array( + ( + (E, S), + (N, W), + ) +) # Loop covering only 3 cells in 2x2 -routing_loop_2x2_tri = np_array(((E, S), (S, NW),)) +routing_loop_2x2_tri = np_array( + ( + (E, S), + (S, NW), + ) +) expected_fc_3x3_0_0 = [ [(0, 0), (0, 1), (0, 2)], @@ -118,22 +145,47 @@ ((0, 0), routing_pc_3x3, direction_map, expected_pc_3x3_0_0), ((1, 1), routing_pc_3x3, direction_map, expected_pc_3x3_1_1), # Watersheds with loops - ((0, 0), routing_loop_1x2, direction_map, [[(0, 0), (0, 1)],]), - ((0, 1), routing_loop_1x2, direction_map, [[(0, 1), (0, 0)],]), + ( + (0, 0), + routing_loop_1x2, + direction_map, + [ + [(0, 0), (0, 1)], + ], + ), + ( + (0, 1), + routing_loop_1x2, + direction_map, + [ + [(0, 1), (0, 0)], + ], + ), ( (0, 0), routing_loop_2x2_quad, direction_map, - [[(0, 0), (0, 1), (1, 1), (1, 0)],], + [ + [(0, 0), (0, 1), (1, 1), (1, 0)], + ], ), ( (1, 1), routing_loop_2x2_quad, direction_map, - [[(1, 1), (1, 0), (0, 0), (0, 1)],], + [ + [(1, 1), (1, 0), (0, 0), (0, 1)], + ], ), ((0, 0), routing_loop_2x2_tri, direction_map, []), - ((1, 1), routing_loop_2x2_tri, direction_map, [[(1, 1), (1, 0), (0, 1)],],), + ( + (1, 1), + routing_loop_2x2_tri, + direction_map, + [ + [(1, 1), (1, 0), (0, 1)], + ], + ), ), ) def test_build_watershed_streams(mouth, routing, direction_map, expected): diff --git a/ce/tests/api/streamflow/watershed/conftest.py b/ce/tests/api/streamflow/watershed/conftest.py index 0b0866bf..3eae3adb 100644 --- a/ce/tests/api/streamflow/watershed/conftest.py +++ b/ce/tests/api/streamflow/watershed/conftest.py @@ -28,7 +28,14 @@ def flow_direction_1(longitudes_1, latitudes_1): return VicDataGrid( longitudes=longitudes_1, # len = 3 latitudes=latitudes_1, # len = 4 - values=np_array(((SE, S, SW), (W, S, W), (N, SW, S), (OUTLET, E, E),)), + values=np_array( + ( + (SE, S, SW), + (W, S, W), + (N, SW, S), + (OUTLET, E, E), + ) + ), ) @@ -37,7 +44,14 @@ def elevation_1(longitudes_1, latitudes_1): return VicDataGrid( longitudes=longitudes_1, # len = 3 latitudes=latitudes_1, # len = 4 - values=np_array(((3, 4, 3), (2, 2, 3), (3, 1, 3), (0, 3, 2),)), + values=np_array( + ( + (3, 4, 3), + (2, 2, 3), + (3, 1, 3), + (0, 3, 2), + ) + ), units="m", ) @@ -48,7 +62,12 @@ def elevation_max_1(longitudes_1, latitudes_1): longitudes=longitudes_1, # len = 3 latitudes=latitudes_1, # len = 4 values=np_array( - ((3.1, 4.1, 3.1), (2.1, 2.1, 3.1), (3.1, 1.1, 3.1), (0.1, 3.1, 2.1),) + ( + (3.1, 4.1, 3.1), + (2.1, 2.1, 3.1), + (3.1, 1.1, 3.1), + (0.1, 3.1, 2.1), + ) ), units="m", ) @@ -60,7 +79,12 @@ def elevation_min_1(longitudes_1, latitudes_1): longitudes=longitudes_1, # len = 3 latitudes=latitudes_1, # len = 4 values=np_array( - ((2.9, 3.9, 2.9), (1.9, 1.9, 2.9), (2.9, 0.9, 2.9), (0, 2.9, 1.9),) + ( + (2.9, 3.9, 2.9), + (1.9, 1.9, 2.9), + (2.9, 0.9, 2.9), + (0, 2.9, 1.9), + ) ), units="m", ) diff --git a/ce/tests/api/streamflow/watershed/test_downstream_watershed.py b/ce/tests/api/streamflow/watershed/test_downstream_watershed.py index 19f8d0dc..fdd23117 100644 --- a/ce/tests/api/streamflow/watershed/test_downstream_watershed.py +++ b/ce/tests/api/streamflow/watershed/test_downstream_watershed.py @@ -54,7 +54,13 @@ ), ) def test_downstream_worker( - lon, lat, expected, flow_direction_1, + lon, + lat, + expected, + flow_direction_1, ): - result = downstream_worker((lon, lat), flow_direction_1,) + result = downstream_worker( + (lon, lat), + flow_direction_1, + ) check_dict_subset(expected, result) diff --git a/ce/tests/api/streamflow/watershed/test_watershed.py b/ce/tests/api/streamflow/watershed/test_watershed.py index a93ee563..af813fdf 100644 --- a/ce/tests/api/streamflow/watershed/test_watershed.py +++ b/ce/tests/api/streamflow/watershed/test_watershed.py @@ -20,7 +20,10 @@ "elevation_units": "m", "area_units": "m^2", }, - "melton_ratio": {"units": "km/km", "value": 1.5496543393378315,}, + "melton_ratio": { + "units": "km/km", + "value": 1.5496543393378315, + }, "boundary": { # TODO: more here "type": "Feature", @@ -48,7 +51,10 @@ "elevation_units": "m", "area_units": "m^2", }, - "melton_ratio": {"units": "km/km", "value": 1.3063945294843617,}, + "melton_ratio": { + "units": "km/km", + "value": 1.3063945294843617, + }, "boundary": { # TODO: more here "type": "Feature", @@ -76,7 +82,10 @@ "elevation_units": "m", "area_units": "m^2", }, - "melton_ratio": {"units": "km/km", "value": 0.692820323027551,}, + "melton_ratio": { + "units": "km/km", + "value": 0.692820323027551, + }, "boundary": { # TODO: more here "type": "Feature", diff --git a/ce/tests/api/streamflow/watershed/test_watershed_streams.py b/ce/tests/api/streamflow/watershed/test_watershed_streams.py index e7ae28cd..1b83f1d7 100644 --- a/ce/tests/api/streamflow/watershed/test_watershed_streams.py +++ b/ce/tests/api/streamflow/watershed/test_watershed_streams.py @@ -25,7 +25,10 @@ { "streams": { "type": "Feature", - "geometry": {"coordinates": (), "type": "MultiLineString",}, + "geometry": { + "coordinates": (), + "type": "MultiLineString", + }, }, }, ), @@ -84,7 +87,13 @@ ), ) def test_worker( - lon, lat, expected, flow_direction_1, + lon, + lat, + expected, + flow_direction_1, ): - result = worker((lon, lat), flow_direction_1,) + result = worker( + (lon, lat), + flow_direction_1, + ) check_dict_subset(expected, result) diff --git a/ce/tests/geo_data_grid_2d/vic/test_vic.py b/ce/tests/geo_data_grid_2d/vic/test_vic.py index 94a50f9b..b5f74ff1 100644 --- a/ce/tests/geo_data_grid_2d/vic/test_vic.py +++ b/ce/tests/geo_data_grid_2d/vic/test_vic.py @@ -7,7 +7,10 @@ "args, expected", [ # Uniform coordinate steps - (dict(longitudes=(0, 1, 2), latitudes=(0, 2, 4)), (1, 2),), + ( + dict(longitudes=(0, 1, 2), latitudes=(0, 2, 4)), + (1, 2), + ), # Nonuniform longitude steps ( dict(longitudes=(0, 1, 3), latitudes=(0, 2, 4)), @@ -97,17 +100,47 @@ def test_is_compatible(grid_1_args, grid_2_args, expected): @pytest.mark.parametrize( - "lon, y", ((0.051, 0), (0.1, 0), (0.149, 0), (0.151, 1), (0.2, 1), (0.249, 1),) + "lon, y", + ( + (0.051, 0), + (0.1, 0), + (0.149, 0), + (0.151, 1), + (0.2, 1), + (0.249, 1), + ), ) @pytest.mark.parametrize( - "lat, x", ((50.11, 0), (50.2, 0), (50.29, 0), (50.31, 1), (50.4, 1), (50.49, 1),) + "lat, x", + ( + (50.11, 0), + (50.2, 0), + (50.29, 0), + (50.31, 1), + (50.4, 1), + (50.49, 1), + ), ) def test_lonlat_to_xy(lon, lat, x, y, vic_data_grid_1): assert vic_data_grid_1.lonlat_to_xy((lon, lat)) == (x, y) -@pytest.mark.parametrize("y, lon", ((0, 0.1), (1, 0.2), (2, 0.3),)) -@pytest.mark.parametrize("x, lat", ((0, 50.2), (1, 50.4), (3, 50.8),)) +@pytest.mark.parametrize( + "y, lon", + ( + (0, 0.1), + (1, 0.2), + (2, 0.3), + ), +) +@pytest.mark.parametrize( + "x, lat", + ( + (0, 50.2), + (1, 50.4), + (3, 50.8), + ), +) def test_xy_to_lonlat(x, y, lon, lat, vic_data_grid_1): assert vic_data_grid_1.xy_to_lonlat((x, y)) == (lon, lat) diff --git a/ce/tests/helpers/test_utils.py b/ce/tests/helpers/test_utils.py index daaacae3..59f1bb9f 100644 --- a/ce/tests/helpers/test_utils.py +++ b/ce/tests/helpers/test_utils.py @@ -65,7 +65,7 @@ def is_dict_subset(dict1, dict2): "Subset" here means that every key in dict1 is in dict2, recursively for values that are themselves dicts. For values that are not dicts, then the values must be equal. - """ + """ def compare(value1, value2): if type(value1) == dict: diff --git a/ce/tests/test_util.py b/ce/tests/test_util.py index 452096aa..57a0c004 100644 --- a/ce/tests/test_util.py +++ b/ce/tests/test_util.py @@ -110,7 +110,7 @@ def test_open_nc(local, online): @pytest.mark.online -@pytest.mark.parametrize(("bad_path"), [("/bad/path/to/file.nc")]) +@pytest.mark.parametrize(("bad_path"), ["/bad/path/to/file.nc"]) def test_open_nc_exception(bad_path): with pytest.raises(Exception): with open_nc(bad_path) as nc: diff --git a/ce/views.py b/ce/views.py index 151d7ba0..b10d48f8 100644 --- a/ce/views.py +++ b/ce/views.py @@ -7,7 +7,7 @@ def add_routes(app): db = SQLAlchemy(app) - @app.route("/api/", methods= ['GET', 'POST']) + @app.route("/api/", methods=["GET", "POST"]) def api_request(*args, **kwargs): return ce.api.call(db.session, *args, **kwargs) From 3eee5e6268a4f7271df92667eea85fe0f03ffa9f Mon Sep 17 00:00:00 2001 From: Lee Zeman Date: Tue, 17 Jun 2025 18:11:37 -0700 Subject: [PATCH 2/3] update pypi publish workflow --- .github/workflows/black.yml | 14 ++++++++++++++ .github/workflows/pypi-publish.yml | 21 +++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 00000000..26e7cde9 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,14 @@ +name: Black + +on: push + +jobs: + black: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: psf/black@stable + with: + options: "--check" \ No newline at end of file diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index d89cc25a..2227da5e 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -4,6 +4,7 @@ on: push: tags: - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+\.dev[0-9]+' jobs: publish: @@ -16,14 +17,18 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.12' - - name: Install dependencies + - name: Install Poetry run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine sphinx - - name: Build and publish + curl -sSL https://install.python-poetry.org | python3 - + - name: Install and publish project env: - TWINE_USERNAME: ${{ secrets.pcic_at_pypi_username }} - TWINE_PASSWORD: ${{ secrets.pcic_at_pypi_password }} + PCIC_PYPI_USERNAME: ${{ secrets.pcic_at_pypi_username }} + PCIC_PYPI_PASSWORD: ${{ secrets.pcic_at_pypi_password }} run: | - python setup.py sdist bdist_wheel - twine upload --repository-url https://pypi.pacificclimate.org/ --skip-existing -u $TWINE_USERNAME -p $TWINE_PASSWORD dist/* + # Configure Poetry to publish to PCIC private package repository + poetry config repositories.pcic https://pypi.pacificclimate.org/ + poetry config http-basic.pcic $PCIC_PYPI_USERNAME $PCIC_PYPI_PASSWORD + # Install, build and publish + poetry install + poetry build + poetry publish -r pcic From ec1d6f6a1c91d93d59df620fa9fb72c006fe3e20 Mon Sep 17 00:00:00 2001 From: Lee Zeman Date: Wed, 18 Jun 2025 17:30:42 -0700 Subject: [PATCH 3/3] skip install --- .github/workflows/pypi-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 2227da5e..5f8a0159 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -29,6 +29,6 @@ jobs: poetry config repositories.pcic https://pypi.pacificclimate.org/ poetry config http-basic.pcic $PCIC_PYPI_USERNAME $PCIC_PYPI_PASSWORD # Install, build and publish - poetry install + # poetry install poetry build poetry publish -r pcic