From 355034b3f6eb7355b38ea002bbefed14bb5a1fb4 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 13:34:49 +0000 Subject: [PATCH 01/14] FIX: Black formatting fixes --- ogc/ogc_common.py | 4 +--- ogc/version.py | 10 ++-------- ogc/wcs_response_1_0_0.py | 16 ++++------------ ogc/wms_response_1_3_0.py | 4 +--- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/ogc/ogc_common.py b/ogc/ogc_common.py index 20b3424..fd0856d 100755 --- a/ogc/ogc_common.py +++ b/ogc/ogc_common.py @@ -123,7 +123,5 @@ def to_xml(self): {exception_text} -""".format( - self=self, exception_text=exception_text - ) +""".format(self=self, exception_text=exception_text) return xml diff --git a/ogc/version.py b/ogc/version.py index 25703f5..b3dac1b 100755 --- a/ogc/version.py +++ b/ogc/version.py @@ -64,14 +64,8 @@ def version(): except Exception: return version_full - version_full = ( - subprocess.check_output([git, "describe", "--always"], cwd=CWD) - .strip() - .decode("ascii") - ) - version_full = version_full.replace("-", "+", 1).replace( - "-", "." - ) # Make this consistent with PEP440 + version_full = subprocess.check_output([git, "describe", "--always"], cwd=CWD).strip().decode("ascii") + version_full = version_full.replace("-", "+", 1).replace("-", ".") # Make this consistent with PEP440 except Exception as e: print("Could not determine Project version from git repo.\n" + str(e)) diff --git a/ogc/wcs_response_1_0_0.py b/ogc/wcs_response_1_0_0.py index 6e281c1..d90813f 100755 --- a/ogc/wcs_response_1_0_0.py +++ b/ogc/wcs_response_1_0_0.py @@ -122,9 +122,7 @@ def coverage_offering(self, coverage): xml += """ {coverage.wgs84_bounding_box_lower_corner_lat_lon[1]} {coverage.wgs84_bounding_box_lower_corner_lat_lon[0]} {coverage.wgs84_bounding_box_upper_corner_lat_lon[1]} {coverage.wgs84_bounding_box_upper_corner_lat_lon[0]} - """.format( - coverage=coverage - ) + """.format(coverage=coverage) xml += """\ @@ -235,9 +233,7 @@ def service(self): UNAVAILABLE {constraints} -""".format( - self=self, constraints=settings.CONSTRAINTS - ) +""".format(self=self, constraints=settings.CONSTRAINTS) base_url = tl.Unicode(default_value=None, allow_none=True) # e.g., http://hostname:port/path? @@ -277,9 +273,7 @@ def capability(self): application/vnd.ogc.se_xml - """.format( - self=self - ) + """.format(self=self) coverages = tl.List(tl.Instance(klass=Coverage)) # is populated via Traits in constructor @@ -316,9 +310,7 @@ def contents(self): {coverage.wgs84_bounding_box_lower_corner_lat_lon[1]} {coverage.wgs84_bounding_box_lower_corner_lat_lon[0]} {coverage.wgs84_bounding_box_upper_corner_lat_lon[1]} {coverage.wgs84_bounding_box_upper_corner_lat_lon[0]} -""".format( - coverage=coverage - ) +""".format(coverage=coverage) xml += " \n" xml += " \n" diff --git a/ogc/wms_response_1_3_0.py b/ogc/wms_response_1_3_0.py index cc56a14..b20f7a1 100755 --- a/ogc/wms_response_1_3_0.py +++ b/ogc/wms_response_1_3_0.py @@ -86,9 +86,7 @@ def request(self): - """.format( - self=self - ) + """.format(self=self) def exception(self): return """\ From 19092f0474ffeba281bb62d1561dfe5aad14183a Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 13:49:46 +0000 Subject: [PATCH 02/14] FIX: E741 ambiguous single-letter variable names --- example/app.py | 2 +- ogc/wcs_request_1_0_0.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/app.py b/example/app.py index f0ddf19..48c0908 100755 --- a/example/app.py +++ b/example/app.py @@ -115,7 +115,7 @@ def check_layers(layer): if match_object: clean_layer = match_object.group(0) - if clean_layer in [l.identifier for l in all_layers]: + if clean_layer in [available_layer.identifier for available_layer in all_layers]: return "{} is an available layer id".format(clean_layer) else: return "No layer available with that id" diff --git a/ogc/wcs_request_1_0_0.py b/ogc/wcs_request_1_0_0.py index 36db9f0..cb84ced 100755 --- a/ogc/wcs_request_1_0_0.py +++ b/ogc/wcs_request_1_0_0.py @@ -160,7 +160,7 @@ def validate(self): self.domain_subset_bbox.lower_corner[1], self.domain_subset_bbox.upper_corner[1], ] - if any([abs(l) > 361.000 for l in lons]) or any([abs(l) > 91.000 for l in lats]): + if any([abs(lon) > 361.000 for lon in lons]) or any([abs(lat) > 91.000 for lat in lats]): raise ogc_common.WCSException( exception_code="InvalidParameterValue", locator="BBOX", From 12140ec6e92f67e6b519e7fdb19a02fca159b3a7 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 13:51:12 +0000 Subject: [PATCH 03/14] FIX: E266 too many leading '#' for block comment --- ogc/version.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ogc/version.py b/ogc/version.py index b3dac1b..1af9b30 100755 --- a/ogc/version.py +++ b/ogc/version.py @@ -12,13 +12,13 @@ import os from collections import OrderedDict -######################### -## UPDATE VERSION HERE ## -######################### +####################### +# UPDATE VERSION HERE # +####################### MAJOR = 0 MINOR = 4 HOTFIX = 2 -######################### +####################### VERSION_INFO = OrderedDict([("MAJOR", MAJOR), ("MINOR", MINOR), ("HOTFIX", HOTFIX)]) From fff9a488b5432141022154ec5212944e59c10526 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 13:53:01 +0000 Subject: [PATCH 04/14] FIX: E401 multiple imports on one line --- ogc/ogc_common.py | 3 ++- ogc/wcs_request_1_0_0.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ogc/ogc_common.py b/ogc/ogc_common.py index fd0856d..74a0270 100755 --- a/ogc/ogc_common.py +++ b/ogc/ogc_common.py @@ -1,6 +1,7 @@ import logging -import lxml, lxml.etree +import lxml +import lxml.etree import numpy as np import traitlets as tl diff --git a/ogc/wcs_request_1_0_0.py b/ogc/wcs_request_1_0_0.py index cb84ced..7c500b3 100755 --- a/ogc/wcs_request_1_0_0.py +++ b/ogc/wcs_request_1_0_0.py @@ -1,6 +1,7 @@ import logging -import lxml, lxml.etree +import lxml +import lxml.etree import dateutil.parser import traitlets as tl From d38bea7e604ab00de6dee4ad514e282ca75a4fb8 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 14:49:03 +0000 Subject: [PATCH 05/14] CHORE: Remove passing formatting checks on failure --- .github/workflows/github-python-workflow.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/github-python-workflow.yml b/.github/workflows/github-python-workflow.yml index e3ed9c4..b332f3f 100644 --- a/.github/workflows/github-python-workflow.yml +++ b/.github/workflows/github-python-workflow.yml @@ -3,9 +3,9 @@ name: Python Workflow on: push: - branches: [main, develop] + branches: [ main, develop ] pull_request: - types: [opened, synchronize, reopened] + types: [ opened, synchronize, reopened ] jobs: lint: @@ -30,12 +30,10 @@ jobs: - name: Run flake8 lint checks run: | # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --output-file=flake8-report.txt - # exit-zero treats all errors as warnings - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics --output-file=flake8-report.txt + flake8 . --count --select=E9,F --show-source --statistics --max-line-length=120 --output-file=flake8-report.txt - name: Run black format checks - run: black --check --diff ogc example > black-report.txt || true # For now pass even if files need formatting + run: black --check --diff ogc example > black-report.txt - name: Artifact flake8 report uses: actions/upload-artifact@v4 @@ -86,7 +84,7 @@ jobs: sonarqube_scan: name: SonarQube Scan - needs: [unit_testing] + needs: [ unit_testing ] runs-on: ubuntu-latest steps: From ef72d7bcf71736f2fdff7e0c270a2ff6981cc630 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Tue, 21 Apr 2026 14:52:28 +0000 Subject: [PATCH 06/14] FIX: Sonarqube remove list comprehension with any --- ogc/wcs_request_1_0_0.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogc/wcs_request_1_0_0.py b/ogc/wcs_request_1_0_0.py index 7c500b3..c9dcf1a 100755 --- a/ogc/wcs_request_1_0_0.py +++ b/ogc/wcs_request_1_0_0.py @@ -161,7 +161,7 @@ def validate(self): self.domain_subset_bbox.lower_corner[1], self.domain_subset_bbox.upper_corner[1], ] - if any([abs(lon) > 361.000 for lon in lons]) or any([abs(lat) > 91.000 for lat in lats]): + if any(abs(lon) > 361.000 for lon in lons) or any(abs(lat) > 91.000 for lat in lats): raise ogc_common.WCSException( exception_code="InvalidParameterValue", locator="BBOX", From e74fce3108f773b15367c927b965cb2d5ef67336 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 12:41:15 +0000 Subject: [PATCH 07/14] Improve linting and formatting checks --- .github/workflows/github-python-workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-python-workflow.yml b/.github/workflows/github-python-workflow.yml index b332f3f..3e3e5a9 100644 --- a/.github/workflows/github-python-workflow.yml +++ b/.github/workflows/github-python-workflow.yml @@ -30,10 +30,10 @@ jobs: - name: Run flake8 lint checks run: | # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F --show-source --statistics --max-line-length=120 --output-file=flake8-report.txt + flake8 . --count --ignore=E,W,D,I,N806,N815,N818,Q000,Q001,Q002,S001,B008,B028 --show-source --statistics --max-line-length=120 --output-file=flake8-report.txt - name: Run black format checks - run: black --check --diff ogc example > black-report.txt + run: black --check --diff -l 120 ogc example > black-report.txt - name: Artifact flake8 report uses: actions/upload-artifact@v4 From 4cef1c2d1415fc1e704de2f8b492d10db60e17f1 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 12:42:43 +0000 Subject: [PATCH 08/14] Add flake8 bugbear and builtins --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 28c2688..e7d6cc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,8 @@ dev = [ "pre_commit", "black", "flake8", + "flake8-bugbear", + "flake8-builtins", ] [tool.setuptools.packages.find] From b875cc71064af8b67863e3c514965b64ba825188 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:00:41 +0000 Subject: [PATCH 09/14] FIX: B006 Remove mutable data structures as argument defaults --- ogc/core.py | 3 ++- ogc/edr/edr_formatter.py | 4 ++-- ogc/edr/test/test_edr_routes.py | 7 ++++--- ogc/servers.py | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ogc/core.py b/ogc/core.py index cbbb175..dbb40b3 100755 --- a/ogc/core.py +++ b/ogc/core.py @@ -38,8 +38,9 @@ class OGC(tl.HasTraits): def base_url(self): return "{}{}?".format(self.server_address, self.endpoint) - def __init__(self, layers=[], **kwargs): + def __init__(self, layers=None, **kwargs): super().__init__(**kwargs) + layers = layers if layers is not None else [] coverages = [ wcs_response_1_0_0.Coverage( layer=layer, diff --git a/ogc/edr/edr_formatter.py b/ogc/edr/edr_formatter.py index e3f3aab..104d570 100644 --- a/ogc/edr/edr_formatter.py +++ b/ogc/edr/edr_formatter.py @@ -17,13 +17,13 @@ def __init__(self, formatter_def: dict): super().__init__(formatter_def) self.mimetype = formatter_def["mimetype"] - def write(self, options: dict = {}, data: dict | None = None) -> str: + def write(self, options: dict | None = None, data: dict | None = None) -> str: """Generate data in the specified format. Parameters ---------- options : dict, optional - Formatting options, by default {}. + Formatting options, by default None. data : dict | None, optional Dictionary representation of the data, by default None. diff --git a/ogc/edr/test/test_edr_routes.py b/ogc/edr/test/test_edr_routes.py index e9eb11f..3b98258 100644 --- a/ogc/edr/test/test_edr_routes.py +++ b/ogc/edr/test/test_edr_routes.py @@ -12,20 +12,21 @@ from ogc.edr.edr_routes import EdrRoutes -def mock_request(request_args: Dict[str, Any] = {}) -> APIRequest: +def mock_request(request_args: Dict[str, Any] | None = None) -> APIRequest: """Creates a mock request for EDR routes to use. Parameters ---------- - request_args: Dict[str, Any], optional - The dictionary for query string arguments. + request_args: Dict[str, Any] | None, optional + The dictionary for query string arguments, by default None. Returns ------- APIRequest Mock API request for route testing. """ + request_args = request_args if request_args is not None else {} environ = create_environ(base_url="http://127.0.0.1:5000/ogc/edr") request = Request(environ) request.args = ImmutableMultiDict(request_args.items()) diff --git a/ogc/servers.py b/ogc/servers.py index d56a2f0..e437c45 100755 --- a/ogc/servers.py +++ b/ogc/servers.py @@ -343,6 +343,6 @@ class FastAPI(object): https://mangum.io/asgi-frameworks/ """ - def __init__(self, *args, ogcs=[]): + def __init__(self, *args, ogcs): super().__init__(*args) raise NotImplementedError From ced82ee3516dfe76952c485742091d1aff90bd9e Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:05:49 +0000 Subject: [PATCH 10/14] FIX: A002 Name shadowing --- ogc/servers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ogc/servers.py b/ogc/servers.py index e437c45..2b93f15 100755 --- a/ogc/servers.py +++ b/ogc/servers.py @@ -258,13 +258,13 @@ def ogc_render(self, ogc_idx): ee = WCSException() return respond_xml(ee.to_xml(), status=500) - def edr_render(self, callable: Callable) -> Callable: + def edr_render(self, request_handler: Callable) -> Callable: """Function which returns a wrapper for the provided callable. Filters arguments and handles any necessary exceptions. Parameters ---------- - callable : Callable + request_handler : Callable The callable request handler to be wrapped. Returns ------- @@ -315,7 +315,7 @@ def wrapper(*args, **kwargs) -> Response: request.args = ImmutableMultiDict(filtered_args) pygeoapi_request = APIRequest.from_flask(request, ["en"]) # Build the flask response - headers, status, content = callable(pygeoapi_request, *args, **kwargs) + headers, status, content = request_handler(pygeoapi_request, *args, **kwargs) response = make_response(content, status) if headers: response.headers = headers From 118f5396964d8bdfc29502b2d939b5090d9b6b19 Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:13:09 +0000 Subject: [PATCH 11/14] FIX: B023 Function definition does not bind loop variable --- ogc/servers.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ogc/servers.py b/ogc/servers.py index 2b93f15..f12ca5a 100755 --- a/ogc/servers.py +++ b/ogc/servers.py @@ -98,16 +98,17 @@ def __init__(self, *args, ogcs=None, home_func=None): ogcs = [] self.ogcs = ogcs + + def make_method(idx): + def method(): + return self.ogc_render(idx) + + return method + for idx, ogc in enumerate(ogcs): endpoint = ogc.endpoint # should be a string, e.g. "/ogc" method_name = "render" + endpoint.replace("/", "_") # e.g. "render_ogc" - def make_method(idx): - def method(): - return self.ogc_render(idx) - - return method - method = make_method(idx) setattr(self, method_name, method) method = getattr(self, method_name) From 3b3e7f0583940ad97314e5298eb79828473c55bb Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:14:40 +0000 Subject: [PATCH 12/14] FIX: A002 Name shadowing --- ogc/wms_response_1_3_0.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ogc/wms_response_1_3_0.py b/ogc/wms_response_1_3_0.py index b20f7a1..4e195f1 100755 --- a/ogc/wms_response_1_3_0.py +++ b/ogc/wms_response_1_3_0.py @@ -239,11 +239,11 @@ def to_xml(self): ) @staticmethod - def _format_number(input, float_decimals=9): - if isinstance(input, float): - return "{number:.{decimals}f}".format(number=input, decimals=float_decimals) + def _format_number(input_number, float_decimals=9): + if isinstance(input_number, float): + return "{number:.{decimals}f}".format(number=input_number, decimals=float_decimals) else: - return "{}".format(input) + return "{}".format(input_number) def _get_CRS_and_BoundingBox(self, depth=3): indent = " " From a823a1b1215c8b89b55e5e5c6c9259e777e6b18a Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:20:09 +0000 Subject: [PATCH 13/14] FIX: B042 Exception class with should pass all args... --- ogc/ogc_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogc/ogc_common.py b/ogc/ogc_common.py index 74a0270..4bf3dd4 100755 --- a/ogc/ogc_common.py +++ b/ogc/ogc_common.py @@ -103,7 +103,7 @@ def __init__( """ exception_code: 'NoApplicableCode', 'InvalidFormat', 'CoverageNotDefined', 'MissingParameterValue', 'InvalidParameterValue' """ - super(WCSException, self).__init__(exception_text) + super().__init__(exception_text, exception_code, locator) self.exception_text = exception_text self.exception_code = exception_code From 885a4b5a9b08e3e3b00e44ff3a374e81102b41bf Mon Sep 17 00:00:00 2001 From: Sam Cranford Date: Fri, 24 Apr 2026 13:22:08 +0000 Subject: [PATCH 14/14] FIX: B018 Found useless Tuple expression --- ogc/wcs_request_1_0_0.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogc/wcs_request_1_0_0.py b/ogc/wcs_request_1_0_0.py index c9dcf1a..0f7fcc9 100755 --- a/ogc/wcs_request_1_0_0.py +++ b/ogc/wcs_request_1_0_0.py @@ -150,7 +150,7 @@ def validate(self): assert self.identifier, "WCS Request validation error: no coverage specified" assert self.domain_subset_bbox, "WCS Request validation error: no bounding box specified" - self.output_format.validate(), "WCS Request validation error: output format" + self.output_format.validate() # WCS Request validation error: output format assert self.height, "WCS Request validation error: no height specified" assert self.width, "WCS Request validation error: no width specified" lons = [