diff --git a/.devcontainer/dev_container.dockerfile b/.devcontainer/dev_container.dockerfile
index f602887..c3b115b 100644
--- a/.devcontainer/dev_container.dockerfile
+++ b/.devcontainer/dev_container.dockerfile
@@ -5,21 +5,16 @@ FROM ${BASE_URL}
USER root
# We'll be running as a non-root user in a container and may want root permissions
-RUN apt update && apt -y install nano ssh sudo && apt clean
-
-# Install setup tools and dependencies
-WORKDIR /app
-COPY . /app
-RUN pip install --upgrade pip setuptools && pip install .[dev]
+RUN apt update && apt -y install --no-install-recommends nano ssh sudo && apt clean
# Set up user to match the host OS (https://stackoverflow.com/a/78621662/415551)
ARG HOST_USER
ARG HOST_UID
ARG HOST_GID
-RUN addgroup --gid ${HOST_GID} ${HOST_USER} \
- && adduser --gecos "" --disabled-password --uid ${HOST_UID} --gid ${HOST_GID} ${HOST_USER} \
- && usermod -aG sudo ${HOST_USER} \
+RUN addgroup --gid "${HOST_GID}" "${HOST_USER}" \
+ && adduser --gecos "" --disabled-password --uid "${HOST_UID}" --gid "${HOST_GID}" "${HOST_USER}" \
+ && usermod -aG sudo "${HOST_USER}" \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
ENV HOME /home/${HOST_USER}
@@ -27,3 +22,4 @@ ENV TMPDIR=/tmp
WORKDIR /home/${HOST_USER}
USER ${HOST_USER}
+ENV PATH "/home/${HOST_USER}/.local/bin:$PATH"
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 9c1d714..fb8fddb 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -39,11 +39,11 @@
]
}
}
- }
+ },
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Uncomment the next line to run commands after the container is created.
- // "postCreateCommand": "cat /etc/os-release",
+ "postCreateCommand": "pip install --upgrade pip setuptools && pip install --no-cache-dir -e .[dev]"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
diff --git a/example/app.py b/example/app.py
index a8e7424..10a285f 100755
--- a/example/app.py
+++ b/example/app.py
@@ -110,13 +110,13 @@ def api_home(endpoint):
# add in some other endpoints.
-@app.route("/")
+@app.route("/", methods=["GET"])
def home():
return """This is an example OGC flask app.
See FULL and PARTIAL endpoints."""
-@app.route("/layers/")
+@app.route("/layers/", methods=["GET"])
def check_layers(layer):
match_object = re.match("[a-zA-Z0-9]+", layer)
diff --git a/ogc/edr/edr_api.py b/ogc/edr/edr_api.py
index b385a64..cb5cc80 100644
--- a/ogc/edr/edr_api.py
+++ b/ogc/edr/edr_api.py
@@ -7,7 +7,7 @@
from functools import wraps
from http import HTTPStatus
from datetime import datetime, timezone
-from typing import Tuple, List, Dict, Any, Union, Callable
+from typing import Tuple, List, Dict, Any, Callable
from traitlets import TraitError
from ogc import podpac as pogc
@@ -328,12 +328,12 @@ def get_collection_edr_query(
return pygeoedr.get_collection_edr_query(api, request, dataset, instance, query_type, location_id)
@staticmethod
- def _temporal_extents(times: List[Union[np.datetime64, datetime]], trs: str | None) -> Dict[str, Any]:
+ def _temporal_extents(times: List[np.datetime64 | datetime], trs: str | None) -> Dict[str, Any]:
"""Get the temporal extents for the provided times and reference system.
Parameters
----------
- times : List[Union[np.datetime64, datetime]]
+ times : List[np.datetime64 | datetime]
Times used to create the temporal extent.
trs : str | None
The reference system for the times.
diff --git a/ogc/edr/test/conftest.py b/ogc/edr/test/conftest.py
index 6da1229..408bf1a 100644
--- a/ogc/edr/test/conftest.py
+++ b/ogc/edr/test/conftest.py
@@ -62,7 +62,7 @@ def set_env_vars():
importlib.reload(settings)
-@pytest.fixture()
+@pytest.fixture
def layers() -> List[pogc.Layer]:
"""List of test layers.
@@ -74,7 +74,7 @@ def layers() -> List[pogc.Layer]:
return [layer1, layer2]
-@pytest.fixture()
+@pytest.fixture
def layers_no_instance() -> List[pogc.Layer]:
"""List of test layers without instances.
@@ -86,7 +86,7 @@ def layers_no_instance() -> List[pogc.Layer]:
return [layer3]
-@pytest.fixture()
+@pytest.fixture
def single_layer_cube_args() -> Dict[str, Any]:
"""Dictionary of valid request arguments that align to a single test layer cube request.
@@ -104,7 +104,7 @@ def single_layer_cube_args() -> Dict[str, Any]:
}
-@pytest.fixture()
+@pytest.fixture
def single_layer_cube_args_internal() -> Dict[str, Any]:
"""Dictionary of valid arguments that align to a single test layer request with internal pygeoapi keys.
@@ -123,7 +123,7 @@ def single_layer_cube_args_internal() -> Dict[str, Any]:
}
-@pytest.fixture()
+@pytest.fixture
def single_layer_cube_args_no_instance_internal() -> Dict[str, Any]:
"""Dictionary of valid arguments that align to a single non-instance test layer request with internal pygeoapi keys.
diff --git a/ogc/edr/test/test_edr_routes.py b/ogc/edr/test/test_edr_routes.py
index 9b245a1..7d5a13a 100644
--- a/ogc/edr/test/test_edr_routes.py
+++ b/ogc/edr/test/test_edr_routes.py
@@ -372,12 +372,13 @@ def test_edr_routes_collection_query_invalid_bbox(layers: List[pogc.Layer], sing
single_layer_cube_args["bbox"] = "invalid"
request = mock_request(single_layer_cube_args)
edr_routes = EdrRoutes(layers=layers)
+ instance = next(iter(layers[0].time_instances()))
with pytest.raises(EDRException) as exception_info:
edr_routes.collection_query(
request,
collection_id=layers[0].group,
- instance_id=next(iter(layers[0].time_instances())),
+ instance_id=instance,
query_type="cube",
)
diff --git a/ogc/test/test_core.py b/ogc/test/test_core.py
index 60be164..2c236b1 100644
--- a/ogc/test/test_core.py
+++ b/ogc/test/test_core.py
@@ -227,16 +227,16 @@ def test_ogc_core_handle_wms_kv_get_capabilities_hierachical_layers():
root = lxml.etree.fromstring(response.encode("utf-8"))
layers = root.xpath(f".//{capability}/{layer}/{title}/text()")
- assert {ogc.service_group_title} == set(layers)
+ assert set(layers) == {ogc.service_group_title}
layers = root.xpath(f".//{capability}/{layer}/{layer}/{title}/text()")
- assert {layer_root.title, layer_nested.group_path[0]} == set(layers)
+ assert set(layers) == {layer_root.title, layer_nested.group_path[0]}
layers = root.xpath(f".//{capability}/{layer}/{layer}/{layer}/{title}/text()")
- assert {layer_nested.group_path[1]} == set(layers)
+ assert set(layers) == {layer_nested.group_path[1]}
layers = root.xpath(f".//{capability}/{layer}/{layer}/{layer}/{layer}/{title}/text()")
- assert {layer_nested.title} == set(layers)
+ assert set(layers) == {layer_nested.title}
def test_ogc_core_handle_wms_kv_get_capabilities_invalid_service():
diff --git a/ogc/test/test_input_security.py b/ogc/test/test_input_security.py
index e8f231b..1ed0963 100644
--- a/ogc/test/test_input_security.py
+++ b/ogc/test/test_input_security.py
@@ -32,7 +32,7 @@ def client():
ogc_instance = core.OGC(layers=[layer])
app = servers.FlaskServer(__name__, ogcs=[ogc_instance])
app.config["TESTING"] = True
- yield app.test_client()
+ return app.test_client()
# ---------------------------------------------------------------------------
diff --git a/ogc/test/test_input_validation.py b/ogc/test/test_input_validation.py
index 0874d3e..bd2e5eb 100644
--- a/ogc/test/test_input_validation.py
+++ b/ogc/test/test_input_validation.py
@@ -11,7 +11,7 @@
from ogc import servers
from ogc import settings
from ogc import podpac as pogc
-from ogc.settings import EDR_TIME_INSTANCE_DIMENSION
+from ogc.settings import EDR_TIME_INSTANCE_DIMENSION, crs_84_uri_format
lat = np.linspace(90, -90, 11)
lon = np.linspace(-180, 180, 21)
@@ -73,7 +73,7 @@ def client():
"""
Create a test client for the Flask server.
- Yields
+ Returns
------
client : FlaskClient
A test client for the Flask server.
@@ -84,7 +84,7 @@ def client():
# Create a FlaskServer instance
app = servers.FlaskServer(__name__, ogcs=[ogc])
app.config.update({"TESTING": True})
- yield app.test_client()
+ return app.test_client()
def make_valid_ogc_wms_get_capabilities_args() -> dict:
@@ -332,7 +332,7 @@ def make_valid_ogc_edr_static_cube_args(layer: str) -> dict:
return {
"f": "CoverageJSON",
"bbox": "-180,-90,180,90",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"parameter-name": layer,
"resolution-x": 512,
"resolution-y": 512,
@@ -355,7 +355,7 @@ def make_valid_ogc_edr_static_area_args(layer: str) -> dict:
return {
"f": "CoverageJSON",
"coords": "POLYGON((-180 90, -180 -90, 180 -90, 180 90, -180 90))",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"parameter-name": layer,
"resolution-x": 512,
"resolution-y": 512,
@@ -378,7 +378,7 @@ def make_valid_ogc_edr_static_position_args(layer: str) -> dict:
return {
"f": "CoverageJSON",
"coords": "POINT(40 50)",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"parameter-name": layer,
}
@@ -401,7 +401,7 @@ def make_valid_ogc_edr_instance_cube_args(layer: str, time: str) -> dict:
return {
"f": "CoverageJSON",
"bbox": "-180,-90,180,90",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"datetime": time,
"parameter-name": layer,
"resolution-x": 512,
@@ -427,7 +427,7 @@ def make_valid_ogc_edr_instance_area_args(layer: str, time: str) -> dict:
return {
"f": "CoverageJSON",
"coords": "POLYGON((-180 90, -180 -90, 180 -90, 180 90, -180 90))",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"datetime": time,
"parameter-name": layer,
"resolution-x": 512,
@@ -453,7 +453,7 @@ def make_valid_ogc_edr_instance_position_args(layer: str, time: str) -> dict:
return {
"f": "CoverageJSON",
"coords": "POINT(40 50)",
- "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
+ "crs": crs_84_uri_format,
"datetime": time,
"parameter-name": layer,
}
diff --git a/ogc/test/test_servers.py b/ogc/test/test_servers.py
index 6bc3423..e8ccc30 100644
--- a/ogc/test/test_servers.py
+++ b/ogc/test/test_servers.py
@@ -19,7 +19,7 @@ def client():
"""
Create a test client for the Flask server.
- Yields
+ Returns
------
client : FlaskClient
A test client for the Flask server.
@@ -44,7 +44,7 @@ def client():
# Create a FlaskServer instance
app = servers.FlaskServer(__name__, ogcs=[ogc])
app.config.update({"TESTING": True})
- yield app.test_client()
+ return app.test_client()
@pytest.fixture
@@ -54,7 +54,7 @@ def client_with_percent_and_caret_layer():
contains a % and a ^, mirroring real-world layer names such as
"Relative_humidity_[%]" and "Total_precipitation_[kg/(m^2)]".
- Yields
+ Returns
------
client : FlaskClient
A test client for the Flask server.
@@ -75,7 +75,7 @@ def client_with_percent_and_caret_layer():
ogc = core.OGC(layers=[layer])
app = servers.FlaskServer(__name__, ogcs=[ogc])
app.config.update({"TESTING": True})
- yield app.test_client()
+ return app.test_client()
@pytest.fixture
diff --git a/ogc/wcs_response_1_0_0.py b/ogc/wcs_response_1_0_0.py
index d25017a..1a76fb3 100755
--- a/ogc/wcs_response_1_0_0.py
+++ b/ogc/wcs_response_1_0_0.py
@@ -174,7 +174,7 @@ def coverage_offering(self, coverage):
xml += "\n".join(
[
" {epsg}".format(epsg=epsg.upper())
- for epsg in list(settings.WCS_CRS.keys())
+ for epsg in settings.WCS_CRS.keys()
]
)
xml += """
diff --git a/ogc/wms_request_1_3_0.py b/ogc/wms_request_1_3_0.py
index 37b9d95..77bd389 100755
--- a/ogc/wms_request_1_3_0.py
+++ b/ogc/wms_request_1_3_0.py
@@ -69,7 +69,7 @@ def validate(self):
lons = [self.bbox.lower_corner[0], self.bbox.upper_corner[0]]
lats = [self.bbox.lower_corner[1], self.bbox.upper_corner[1]]
bbox = lons + lats
- if any([abs(round(x, 9)) > 20037508.342789244 for x in bbox]):
+ if any(abs(round(x, 9)) > 20037508.342789244 for x in bbox):
raise ogc_common.WCSException(
exception_code="InvalidParameterValue",
locator="BBOX",
diff --git a/ogc/wms_response_1_3_0.py b/ogc/wms_response_1_3_0.py
index 51401de..dc20cae 100755
--- a/ogc/wms_response_1_3_0.py
+++ b/ogc/wms_response_1_3_0.py
@@ -169,7 +169,7 @@ def coverage_layer(self, coverage: Coverage, depth: int) -> str:
None,
)
except AttributeError:
- latest_lis_time = next((t for t in reversed(coverage.layer.valid_times)), None)
+ latest_lis_time = next((reversed(coverage.layer.valid_times)), None)
if latest_lis_time is not None:
# default to latest LIS time, if available
@@ -303,7 +303,7 @@ def _get_CRS_and_BoundingBox(self, depth=3):
"\n".join(
[
INDENT * depth + """{epsg}""".format(epsg=epsg.upper())
- for epsg, bbox in list(settings.WMS_CRS.items())
+ for epsg, bbox in settings.WMS_CRS.items()
]
)
+ "\n"
@@ -329,7 +329,7 @@ def _get_CRS_and_BoundingBox(self, depth=3):
maxx=Capabilities._format_number(bbox["maxx"]),
maxy=Capabilities._format_number(bbox["maxy"]),
)
- for epsg, bbox in list(settings.WMS_CRS.items())
+ for epsg, bbox in settings.WMS_CRS.items()
]
)
return output_text
diff --git a/ogc/wmts/test/conftest.py b/ogc/wmts/test/conftest.py
index a331778..826cdda 100644
--- a/ogc/wmts/test/conftest.py
+++ b/ogc/wmts/test/conftest.py
@@ -35,7 +35,7 @@
)
-@pytest.fixture()
+@pytest.fixture
def layers() -> List[pogc.Layer]:
"""List of test layers.
@@ -47,7 +47,7 @@ def layers() -> List[pogc.Layer]:
return [layer1, layer2]
-@pytest.fixture()
+@pytest.fixture
def coverages() -> List[Coverage]:
"""List of coverages based on layers.
diff --git a/ogc/wmts/test/test_wmts_routes.py b/ogc/wmts/test/test_wmts_routes.py
index c71153c..bd9e966 100644
--- a/ogc/wmts/test/test_wmts_routes.py
+++ b/ogc/wmts/test/test_wmts_routes.py
@@ -51,7 +51,8 @@ def test_handle_wmts_kv_get_capabilities_from_ogc_core(layers: List[pogc.Layer])
}
response = ogc.handle_wmts_kv(args)
assert isinstance(response, str)
- assert "Capabilities" in response and "wmts/1.0" in response
+ assert "Capabilities" in response
+ assert "wmts/1.0" in response
def test_handle_kv_get_capabilities(coverages: List[Coverage]):
@@ -71,7 +72,8 @@ def test_handle_kv_get_capabilities(coverages: List[Coverage]):
}
response = wmts_routes.handle_kv(args)
assert isinstance(response, str)
- assert "Capabilities" in response and "wmts/1.0" in response
+ assert "Capabilities" in response
+ assert "wmts/1.0" in response
def test_handle_kv_get_capabilities_invalid_service(coverages: List[Coverage]):