diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 7b7a0f1b51..79f556d844 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -1130,14 +1130,6 @@ "endColumn": 41, "lineCount": 1 } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 20, - "endColumn": 34, - "lineCount": 1 - } } ], "./monitoring/mock_uss/__init__.py": [ @@ -5930,1130 +5922,6 @@ } } ], - "./monitoring/monitorlib/idempotency.py": [ - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 32, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/infrastructure.py": [ - { - "code": "reportArgumentType", - "range": { - "startColumn": 56, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 48, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 38, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportIncompatibleMethodOverride", - "range": { - "startColumn": 8, - "endColumn": 15, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 45, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 32, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 32, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 32, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 32, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 24, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 24, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 22, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 22, - "endColumn": 36, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/kml/f3548v21.py": [ - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 52, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 37, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 5, - "endColumn": 8, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 32, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 30, - "endColumn": 33, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/kml/flight_planning.py": [ - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 5, - "endColumn": 8, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 43, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 43, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 37, - "endColumn": 40, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/kml/generation.py": [ - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 5, - "endColumn": 8, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 31, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 47, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 31, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 47, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 18, - "endColumn": 26, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 24, - "endColumn": 46, - "lineCount": 3 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 24, - "endColumn": 46, - "lineCount": 3 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 37, - "endColumn": 46, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 75, - "endColumn": 84, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 33, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 17, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 17, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 17, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 17, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 55, - "endColumn": 79, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 27, - "endColumn": 30, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/locality.py": [ - { - "code": "reportInvalidTypeVarUse", - "range": { - "startColumn": 52, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportReturnType", - "range": { - "startColumn": 27, - "endColumn": 32, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/multiprocessing.py": [ - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 11, - "endColumn": 32, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 30, - "endColumn": 34, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/mutate/rid.py": [ - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 40, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 27, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 28, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 50, - "endColumn": 82, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 51, - "endColumn": 84, - "lineCount": 1 - } - }, - { - "code": "reportReturnType", - "range": { - "startColumn": 19, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportReturnType", - "range": { - "startColumn": 19, - "endColumn": 34, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 48, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 16, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 33, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 49, - "endColumn": 62, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 16, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 40, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 27, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 28, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 31, - "endColumn": 62, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 38, - "endColumn": 69, - "lineCount": 1 - } - }, - { - "code": "reportGeneralTypeIssues", - "range": { - "startColumn": 15, - "endColumn": 18, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 5, - "endColumn": 21, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 23, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 23, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 27, - "endColumn": 34, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 40, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 28, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 33, - "endColumn": 40, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 41, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/mutate/scd.py": [ - { - "code": "reportReturnType", - "range": { - "startColumn": 24, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 16, - "endColumn": 58, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/rid_automated_testing/injection_api.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 42, - "endColumn": 45, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 40, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 37, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 73, - "endColumn": 86, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 60, - "endColumn": 73, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 171, - "endColumn": 184, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 38, - "endColumn": 51, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 37, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 46, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 66, - "endColumn": 81, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 184, - "endColumn": 199, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 38, - "endColumn": 53, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 12, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 22, - "endColumn": 46, - "lineCount": 1 - } - }, - { - "code": "reportReturnType", - "range": { - "startColumn": 15, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 70, - "endColumn": 78, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 72, - "endColumn": 80, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 59, - "endColumn": 62, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 41, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 70, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 25, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 41, - "endColumn": 44, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 44, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 43, - "endColumn": 51, - "lineCount": 1 - } - }, - { - "code": "reportOptionalOperand", - "range": { - "startColumn": 35, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportOptionalOperand", - "range": { - "startColumn": 33, - "endColumn": 35, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/rid_v1.py": [ - { - "code": "reportReturnType", - "range": { - "startColumn": 15, - "endColumn": 35, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/schema_validation.py": [ - { - "code": "reportPrivateImportUsage", - "range": { - "startColumn": 36, - "endColumn": 41, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/subscription_params.py": [ - { - "code": "reportReturnType", - "range": { - "startColumn": 15, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 22, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 22, - "endColumn": 36, - "lineCount": 1 - } - } - ], - "./monitoring/monitorlib/temporal.py": [ - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 18, - "endColumn": 27, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 24, - "endColumn": 26, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 28, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 22, - "endColumn": 24, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 27, - "endColumn": 42, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 33, - "endColumn": 42, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 39, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 38, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 32, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 125, - "endColumn": 127, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 131, - "endColumn": 134, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 21, - "endColumn": 23, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 26, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 32, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportPossiblyUnboundVariable", - "range": { - "startColumn": 38, - "endColumn": 40, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 21, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 31, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 20, - "endColumn": 26, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 50, - "endColumn": 59, - "lineCount": 1 - } - } - ], "./monitoring/uss_qualifier/action_generators/action_generator.py": [ { "code": "reportInvalidTypeVarUse", diff --git a/monitoring/monitorlib/idempotency.py b/monitoring/monitorlib/idempotency.py index 1edfb152a9..60849bf122 100644 --- a/monitoring/monitorlib/idempotency.py +++ b/monitoring/monitorlib/idempotency.py @@ -46,7 +46,8 @@ def _set_responses(responses: dict[str, Response]) -> bytes: oldest_id = request_id oldest_timestamp = t - del responses[oldest_id] + if oldest_id: + del responses[oldest_id] return s.encode("utf-8") diff --git a/monitoring/monitorlib/infrastructure.py b/monitoring/monitorlib/infrastructure.py index 480ac42276..8c38434f13 100644 --- a/monitoring/monitorlib/infrastructure.py +++ b/monitoring/monitorlib/infrastructure.py @@ -37,11 +37,15 @@ def issue_token(self, intended_audience: str, scopes: list[str]) -> str: raise NotImplementedError() - def get_headers(self, url: str, scopes: list[str] = None) -> dict[str, str]: + def get_headers(self, url: str, scopes: list[str] | None = None) -> dict[str, str]: if scopes is None: scopes = ALL_SCOPES scopes = [s.value if isinstance(s, Enum) else s for s in scopes] intended_audience = urllib.parse.urlparse(url).hostname + + if not intended_audience: + return {} + scope_string = " ".join(scopes) if intended_audience not in self._tokens: self._tokens[intended_audience] = {} @@ -57,8 +61,9 @@ def get_headers(self, url: str, scopes: list[str] = None) -> dict[str, str]: return {"Authorization": "Bearer " + token} def add_headers(self, request: requests.PreparedRequest, scopes: list[str]): - for k, v in self.get_headers(request.url, scopes).items(): - request.headers[k] = v + if request.url: + for k, v in self.get_headers(request.url, scopes).items(): + request.headers[k] = v def get_sub(self) -> str | None: """Retrieve `sub` claim from one of the existing tokens""" @@ -91,7 +96,7 @@ def __init__( self._prefix_url = prefix_url[0:-1] if prefix_url[-1] == "/" else prefix_url self.auth_adapter = auth_adapter - self.default_scopes = None + self.default_scopes: list[str] | None = None self.timeout_seconds = timeout_seconds or CLIENT_TIMEOUT # Overrides method on requests.Session @@ -117,7 +122,7 @@ def adjust_request_kwargs(self, kwargs): def auth( prepared_request: requests.PreparedRequest, ) -> requests.PreparedRequest: - if scopes: + if scopes and self.auth_adapter: self.auth_adapter.add_headers(prepared_request, scopes) return prepared_request @@ -126,11 +131,11 @@ def auth( kwargs["timeout"] = self.timeout_seconds return kwargs - def request(self, method, url, **kwargs): + def request(self, method, url, *args, **kwargs): if "auth" not in kwargs: kwargs = self.adjust_request_kwargs(kwargs) - return super().request(method, url, **kwargs) + return super().request(method, url, *args, **kwargs) def get_prefix_url(self): return self._prefix_url @@ -161,15 +166,16 @@ def __init__( self._prefix_url = prefix_url[0:-1] if prefix_url[-1] == "/" else prefix_url self.auth_adapter = auth_adapter - self.default_scopes = None + self.default_scopes: list[str] | None = None self.timeout_seconds = timeout_seconds or CLIENT_TIMEOUT async def build_session(self): self._client = ClientSession() def close(self): - loop = asyncio.get_event_loop() - loop.run_until_complete(self._client.close()) + if self._client: + loop = asyncio.get_event_loop() + loop.run_until_complete(self._client.close()) def adjust_request_kwargs(self, url, method, kwargs): if self.auth_adapter: @@ -202,6 +208,10 @@ async def put(self, url, **kwargs): url = self._prefix_url + url if "auth" not in kwargs: kwargs = self.adjust_request_kwargs(url, "PUT", kwargs) + + if not self._client: + raise ValueError("Client is not ready") + async with self._client.put(url, **kwargs) as response: return ( response.status, @@ -214,6 +224,10 @@ async def get(self, url, **kwargs): url = self._prefix_url + url if "auth" not in kwargs: kwargs = self.adjust_request_kwargs(url, "GET", kwargs) + + if not self._client: + raise ValueError("Client is not ready") + async with self._client.get(url, **kwargs) as response: return ( response.status, @@ -226,6 +240,10 @@ async def post(self, url, **kwargs): url = self._prefix_url + url if "auth" not in kwargs: kwargs = self.adjust_request_kwargs(url, "POST", kwargs) + + if not self._client: + raise ValueError("Client is not ready") + async with self._client.post(url, **kwargs) as response: return ( response.status, @@ -238,6 +256,10 @@ async def delete(self, url, **kwargs): url = self._prefix_url + url if "auth" not in kwargs: kwargs = self.adjust_request_kwargs(url, "DELETE", kwargs) + + if not self._client: + raise ValueError("Client is not ready") + async with self._client.delete(url, **kwargs) as response: return ( response.status, diff --git a/monitoring/monitorlib/kml/f3548v21.py b/monitoring/monitorlib/kml/f3548v21.py index 7add7bb9d9..55cf0cef5f 100644 --- a/monitoring/monitorlib/kml/f3548v21.py +++ b/monitoring/monitorlib/kml/f3548v21.py @@ -21,14 +21,14 @@ from monitoring.monitorlib.scd import priority_of -def full_op_intent(op_intent: OperationalIntent) -> kml.Folder: +def full_op_intent(op_intent: OperationalIntent): """Render operational intent information into Placemarks in a KML folder.""" ref = op_intent.reference details = op_intent.details name = f"{ref.manager}'s P{priority_of(details)} {ref.state.value} {ref.id}[{ref.version}] @ {ref.ovn}" folder = kml.Folder(kml.name(name)) if "volumes" in details: - for i, v4_f3548 in enumerate(details.volumes): + for i, v4_f3548 in enumerate(details.volumes or []): v4 = Volume4D.from_f3548v21(v4_f3548) folder.append( make_placemark_from_volume( @@ -38,7 +38,7 @@ def full_op_intent(op_intent: OperationalIntent) -> kml.Folder: ) ) if "off_nominal_volumes" in details: - for i, v4_f3548 in enumerate(details.off_nominal_volumes): + for i, v4_f3548 in enumerate(details.off_nominal_volumes or []): v4 = Volume4D.from_f3548v21(v4_f3548) folder.append( make_placemark_from_volume( @@ -53,8 +53,12 @@ def full_op_intent(op_intent: OperationalIntent) -> kml.Folder: def op_intent_refs_query( req: QueryOperationalIntentReferenceParameters, resp: QueryOperationalIntentReferenceResponse, -) -> kml.Placemark: +): """Render the area of interest and response from an operational intent references query into a KML Placemark.""" + + if not req.area_of_interest: + raise ValueError("req.area_of_interest is not defined") + v4 = Volume4D.from_f3548v21(req.area_of_interest) items = "".join( f"
  • {oi.manager}'s {oi.state.value} {oi.id}[{oi.version}]
  • " @@ -68,7 +72,7 @@ def op_intent_refs_query( ) -def f3548v21_styles() -> list[kml.Style]: +def f3548v21_styles() -> list: """Provides KML styles according to F3548-21 operational intent states.""" return [ kml.Style( diff --git a/monitoring/monitorlib/kml/flight_planning.py b/monitoring/monitorlib/kml/flight_planning.py index 5f89dff3bf..34e5cffa14 100644 --- a/monitoring/monitorlib/kml/flight_planning.py +++ b/monitoring/monitorlib/kml/flight_planning.py @@ -15,9 +15,7 @@ ) -def upsert_flight_plan( - req: UpsertFlightPlanRequest, resp: UpsertFlightPlanResponse -) -> kml.Folder: +def upsert_flight_plan(req: UpsertFlightPlanRequest, resp: UpsertFlightPlanResponse): """Render a flight planning action into a KML folder.""" basic_info = req.flight_plan.basic_information folder = kml.Folder( @@ -25,7 +23,7 @@ def upsert_flight_plan( f"Activity {resp.planning_result.value}, flight {resp.flight_plan_status.value}" ) ) - for i, v4_flight_planning in enumerate(basic_info.area): + for i, v4_flight_planning in enumerate(basic_info.area or []): v4 = Volume4D.from_flight_planning_api(v4_flight_planning) folder.append( make_placemark_from_volume( @@ -37,7 +35,7 @@ def upsert_flight_plan( return folder -def flight_planning_styles() -> list[kml.Style]: +def flight_planning_styles() -> list: """Provides KML styles with names in the form {FlightPlanState}_{AirspaceUsageState}.""" return [ kml.Style( diff --git a/monitoring/monitorlib/kml/generation.py b/monitoring/monitorlib/kml/generation.py index 9273e6a843..fd45630d84 100644 --- a/monitoring/monitorlib/kml/generation.py +++ b/monitoring/monitorlib/kml/generation.py @@ -53,7 +53,7 @@ def make_placemark_from_volume( name: str | None = None, style_url: str | None = None, description: str | None = None, -) -> kml.Placemark: +): if "outline_polygon" in v4.volume and v4.volume.outline_polygon: vertices = v4.volume.outline_polygon.vertices elif "outline_circle" in v4.volume and v4.volume.outline_circle: @@ -70,6 +70,9 @@ def make_placemark_from_volume( else: raise NotImplementedError("Volume footprint type not supported") + if not vertices: + raise NotImplementedError("No vertices found") + # Create placemark args = [] if name is not None: @@ -121,7 +124,7 @@ def make_placemark_from_volume( _altitude_mode_of( v4.volume.altitude_lower if v4.volume.altitude_lower - else AltitudeDatum.SFC + else Altitude(reference=AltitudeDatum.SFC) ) ), kml.outerBoundaryIs( @@ -142,7 +145,7 @@ def make_placemark_from_volume( _altitude_mode_of( v4.volume.altitude_upper if v4.volume.altitude_upper - else AltitudeDatum.SFC + else Altitude(reference=AltitudeDatum.SFC) ) ), kml.outerBoundaryIs( @@ -160,6 +163,8 @@ def make_placemark_from_volume( # We can only create the sides of the volume if the altitude references are the same if ( make_sides + and v4.volume.altitude_lower + and v4.volume.altitude_upper and v4.volume.altitude_lower.reference == v4.volume.altitude_upper.reference ): indices = list(range(len(vertices))) @@ -187,7 +192,7 @@ def make_placemark_from_volume( return placemark -def query_styles() -> list[kml.Style]: +def query_styles() -> list: """Provides KML styles for query areas.""" return [ kml.Style( diff --git a/monitoring/monitorlib/locality.py b/monitoring/monitorlib/locality.py index 9232bd7098..f6e8c6d440 100644 --- a/monitoring/monitorlib/locality.py +++ b/monitoring/monitorlib/locality.py @@ -3,7 +3,6 @@ import inspect import sys from abc import ABC, abstractmethod -from typing import TypeVar LocalityCode = str """Case-sensitive string naming a subclass of the Locality base class""" @@ -42,7 +41,7 @@ def __str__(self): return self.__class__.__name__ @staticmethod - def from_locale(locality_code: LocalityCode) -> LocalityType: + def from_locale(locality_code: LocalityCode) -> Locality: current_module = sys.modules[__name__] for name, obj in inspect.getmembers(current_module, inspect.isclass): if issubclass(obj, Locality) and obj != Locality: @@ -53,9 +52,6 @@ def from_locale(locality_code: LocalityCode) -> LocalityType: ) -LocalityType = TypeVar("LocalityType", bound=Locality) - - class Switzerland(Locality): @classmethod def locality_code(cls) -> str: diff --git a/monitoring/monitorlib/multiprocessing.py b/monitoring/monitorlib/multiprocessing.py index e581c71569..d90a39a300 100644 --- a/monitoring/monitorlib/multiprocessing.py +++ b/monitoring/monitorlib/multiprocessing.py @@ -2,6 +2,7 @@ import multiprocessing import multiprocessing.shared_memory from collections.abc import Callable +from multiprocessing.synchronize import RLock as RLockT from typing import Any @@ -24,7 +25,7 @@ class SynchronizedValue: SIZE_BYTES = 4 """Number of bytes at the beginning of the memory buffer dedicated to defining the size of the content.""" - _lock: multiprocessing.RLock + _lock: RLockT _shared_memory: multiprocessing.shared_memory.SharedMemory _encoder: Callable[[Any], bytes] _decoder: Callable[[bytes], Any] @@ -33,7 +34,7 @@ class SynchronizedValue: def __init__( self, initial_value, - capacity_bytes: int = 10e6, + capacity_bytes: int = 10000000, encoder: Callable[[Any], bytes] | None = None, decoder: Callable[[bytes], Any] | None = None, ): diff --git a/monitoring/monitorlib/mutate/rid.py b/monitoring/monitorlib/mutate/rid.py index cd23d20979..c6f3d0a459 100644 --- a/monitoring/monitorlib/mutate/rid.py +++ b/monitoring/monitorlib/mutate/rid.py @@ -1,4 +1,5 @@ import datetime +from typing import Any import s2sphere import uas_standards.astm.f3411.v19.api as v19_api @@ -23,15 +24,19 @@ class ChangedSubscription(RIDQuery): @property def _v19_response(self) -> v19_api.PutSubscriptionResponse: + if not self.v19_query: + raise ValueError("v19 query isn't set") return ImplicitDict.parse( - self.v19_query.response.json, + self.v19_query.response.json or {}, v19_api.PutSubscriptionResponse, ) @property def _v22a_response(self) -> v22a_api.PutSubscriptionResponse: + if not self.v22a_query: + raise ValueError("v22a query isn't set") return ImplicitDict.parse( - self.v22a_query.response.json, + self.v22a_query.response.json or {}, v22a_api.PutSubscriptionResponse, ) @@ -78,9 +83,13 @@ def subscription(self) -> Subscription | None: @property def isas(self) -> list[ISA]: if self.rid_version == RIDVersion.f3411_19: - return [ISA(v19_value=isa) for isa in self._v19_response.service_areas] + return [ + ISA(v19_value=isa) for isa in self._v19_response.service_areas or [] + ] elif self.rid_version == RIDVersion.f3411_22a: - return [ISA(v22a_value=isa) for isa in self._v22a_response.service_areas] + return [ + ISA(v22a_value=isa) for isa in self._v22a_response.service_areas or [] + ] else: raise NotImplementedError( f"Cannot retrieve ISAs using RID version {self.rid_version}" @@ -247,9 +256,9 @@ def rid_version(self) -> RIDVersion: def raw( self, ) -> v19_api.SubscriberToNotify | v22a_api.SubscriberToNotify: - if self.rid_version == RIDVersion.f3411_19: + if self.rid_version == RIDVersion.f3411_19 and self.v19_value: return self.v19_value - elif self.rid_version == RIDVersion.f3411_22a: + elif self.rid_version == RIDVersion.f3411_22a and self.v22a_value: return self.v22a_value else: raise NotImplementedError( @@ -264,8 +273,8 @@ def notify( participant_id: str | None = None, ) -> ISAChangeNotification: # Note that optional `extents` are not specified - if self.rid_version == RIDVersion.f3411_19: - body = { + if self.rid_version == RIDVersion.f3411_19 and self.v19_value: + body: dict[str, Any] = { "subscriptions": self.v19_value.subscriptions, } if isa is not None: @@ -282,7 +291,7 @@ def notify( query_type=QueryType.F3411v19USSPostIdentificationServiceArea, ) ) - elif self.rid_version == RIDVersion.f3411_22a: + elif self.rid_version == RIDVersion.f3411_22a and self.v22a_value: body = { "subscriptions": self.v22a_value.subscriptions, } @@ -320,8 +329,10 @@ class ChangedISA(RIDQuery): def _v19_response( self, ) -> v19_api.PutIdentificationServiceAreaResponse: + if not self.v19_query: + raise ValueError("v19 query isn't set") return ImplicitDict.parse( - self.v19_query.response.json, + self.v19_query.response.json or {}, v19_api.PutIdentificationServiceAreaResponse, ) @@ -329,8 +340,10 @@ def _v19_response( def _v22a_response( self, ) -> v22a_api.PutIdentificationServiceAreaResponse: + if not self.v22a_query: + raise ValueError("v22a query isn't set") return ImplicitDict.parse( - self.v22a_query.response.json, + self.v22a_query.response.json or {}, v22a_api.PutIdentificationServiceAreaResponse, ) @@ -392,13 +405,13 @@ def subscribers(self) -> list[SubscriberToNotify] | None: return None return [ SubscriberToNotify(v19_value=sub) - for sub in self._v19_response.subscribers + for sub in self._v19_response.subscribers or [] ] elif self.rid_version == RIDVersion.f3411_22a: return ( [ SubscriberToNotify(v22a_value=sub) - for sub in self._v22a_response.subscribers + for sub in self._v22a_response.subscribers or [] ] if "subscribers" in self._v22a_response else [] @@ -423,7 +436,7 @@ def sub_ids(self) -> set[str]: return set( [ subscription.subscription_id - for subscriber in self._v22a_response.subscribers + for subscriber in self._v22a_response.subscribers or [] if subscriber is not None for subscription in subscriber.subscriptions ] @@ -456,7 +469,7 @@ def build_isa_request_body( end_time: datetime.datetime, uss_base_url: str, rid_version: RIDVersion, -) -> dict[str, any]: +) -> dict[str, Any]: """Build the http request body expected to PUT or UPDATE an ISA on a DSS, in accordance with the specified rid_version.""" if rid_version == RIDVersion.f3411_19: @@ -490,7 +503,7 @@ def build_isa_request_body( def build_isa_url( rid_version: RIDVersion, isa_id: str, isa_version: str | None = None -) -> (Operation, str): +) -> tuple[Operation, str]: """Build the required URL to create, get, update or delete an ISA on a DSS, in accordance with the specified rid_version and isa_version, if it is available. @@ -591,7 +604,7 @@ def put_isa( do_not_notify = [] notifications = { sub.url: sub.notify(isa.id, utm_client, isa) - for sub in dss_response.subscribers + for sub in dss_response.subscribers or [] if not any(sub.url.startswith(base_url) for base_url in do_not_notify) } else: @@ -647,7 +660,7 @@ def delete_isa( do_not_notify = [] notifications = { sub.url: sub.notify(isa.id, utm_client) - for sub in dss_response.subscribers + for sub in dss_response.subscribers or [] if not any(sub.url.startswith(base_url) for base_url in do_not_notify) } else: @@ -663,8 +676,10 @@ class UpdatedISA(RIDQuery): def _v19_request( self, ) -> v19_api.PutIdentificationServiceAreaNotificationParameters: + if not self.v19_query: + raise ValueError("v19 query isn't set") return ImplicitDict.parse( - self.v19_query.request.json, + self.v19_query.request.json or {}, v19_api.PutIdentificationServiceAreaNotificationParameters, ) @@ -672,8 +687,10 @@ def _v19_request( def _v22a_request( self, ) -> v22a_api.PutIdentificationServiceAreaNotificationParameters: + if not self.v22a_query: + raise ValueError("v22a query isn't set") return ImplicitDict.parse( - self.v22a_query.request.json, + self.v22a_query.request.json or {}, v22a_api.PutIdentificationServiceAreaNotificationParameters, ) @@ -702,9 +719,9 @@ def isa(self) -> ISA | None: @property def isa_id(self) -> str: - if self.rid_version == RIDVersion.f3411_19: + if self.rid_version == RIDVersion.f3411_19 and self.v19_query: url = self.v19_query.request.url - elif self.rid_version == RIDVersion.f3411_22a: + elif self.rid_version == RIDVersion.f3411_22a and self.v22a_query: url = self.v22a_query.request.url else: raise NotImplementedError( diff --git a/monitoring/monitorlib/mutate/scd.py b/monitoring/monitorlib/mutate/scd.py index de168dc7ba..2f6a4b9259 100644 --- a/monitoring/monitorlib/mutate/scd.py +++ b/monitoring/monitorlib/mutate/scd.py @@ -31,16 +31,17 @@ def errors(self) -> list[str]: return [f"Failed to {self.mutation} SCD Subscription ({self.status_code})"] if self.json_result is None: return ["Response did not contain valid JSON"] + return [] @property def subscription(self) -> Subscription | None: - if self.json_result is None: + if self.json_result is None or "subscription" not in self.json_result: return None try: # We get a ValueError if .parse is fed a None, # or if the JSON can't be parsed as a Subscription. return ImplicitDict.parse( - self.json_result.get("subscription", None), + self.json_result["subscription"], Subscription, ) except ValueError: @@ -123,8 +124,8 @@ def build_upsert_subscription_params( base_url: str, notify_for_op_intents: bool, notify_for_constraints: bool, - min_alt_m: float, - max_alt_m: float, + min_alt_m: float | None, + max_alt_m: float | None, ) -> PutSubscriptionParameters: return PutSubscriptionParameters( extents=Volume4D.from_values( diff --git a/monitoring/monitorlib/rid_automated_testing/injection_api.py b/monitoring/monitorlib/rid_automated_testing/injection_api.py index ee99c05146..ef346f2385 100644 --- a/monitoring/monitorlib/rid_automated_testing/injection_api.py +++ b/monitoring/monitorlib/rid_automated_testing/injection_api.py @@ -2,9 +2,9 @@ import arrow import s2sphere -from uas_standards.astm.f3411.v22a.api import UASID from uas_standards.interuss.automated_testing.rid.v1 import injection from uas_standards.interuss.automated_testing.rid.v1.injection import ( + UASID, RIDAircraftState, RIDFlightDetails, UAType, @@ -63,7 +63,10 @@ def __init__(self, *args, **kwargs): continue for mandatory_field in MANDATORY_POSITION_FIELDS: - if telemetry.position.get(mandatory_field, None) is None: + if ( + not telemetry.position + or telemetry.position.get(mandatory_field, None) is None + ): is_ok = False break @@ -92,29 +95,32 @@ def __init__(self, *args, **kwargs): ): detail.details.uas_id = UASID() - if detail.details.uas_id.serial_number: - if not serial_number: # No serial number outside uas_id, we set it - detail.details.serial_number = detail.details.uas_id.serial_number - elif serial_number != detail.details.uas_id.serial_number: - raise ValueError( - f"Impossible to validate test flight: details.serial_number ({serial_number}) is not equal to details.uas_id.serial_number ({detail.details.uas_id.serial_number})" - ) - elif serial_number: # No serial_number is uas_id, but we do have one externally: we do set it in uas_id - detail.details.uas_id.serial_number = serial_number - - if detail.details.uas_id.registration_id: - if ( - not registration_number - ): # No serial number outside uas_id, we set it - detail.details.registration_number = ( - detail.details.uas_id.registration_id - ) - elif registration_number != detail.details.uas_id.registration_id: - raise ValueError( - f"Impossible to validate test flight: details.registration_number ({registration_number}) is not eqal to details.uas_id.registration_id ({detail.details.uas_id.registration_id})" - ) - elif registration_number: # No serial_number is uas_id, but we do have one externally: we do set it in uas_id - detail.details.uas_id.registration_id = registration_number + if detail.details.uas_id is not None: + if detail.details.uas_id.serial_number: + if not serial_number: # No serial number outside uas_id, we set it + detail.details.serial_number = ( + detail.details.uas_id.serial_number + ) + elif serial_number != detail.details.uas_id.serial_number: + raise ValueError( + f"Impossible to validate test flight: details.serial_number ({serial_number}) is not equal to details.uas_id.serial_number ({detail.details.uas_id.serial_number})" + ) + elif serial_number: # No serial_number is uas_id, but we do have one externally: we do set it in uas_id + detail.details.uas_id.serial_number = serial_number + + if detail.details.uas_id.registration_id: + if ( + not registration_number + ): # No serial number outside uas_id, we set it + detail.details.registration_number = ( + detail.details.uas_id.registration_id + ) + elif registration_number != detail.details.uas_id.registration_id: + raise ValueError( + f"Impossible to validate test flight: details.registration_number ({registration_number}) is not eqal to details.uas_id.registration_id ({detail.details.uas_id.registration_id})" + ) + elif registration_number: # No serial_number is uas_id, but we do have one externally: we do set it in uas_id + detail.details.uas_id.registration_id = registration_number def get_span( self, @@ -124,6 +130,7 @@ def get_span( times = [ arrow.get(aircraft_state.timestamp).datetime for aircraft_state in self.telemetry + if aircraft_state.timestamp ] times.extend( arrow.get(details.effective_after).datetime @@ -152,7 +159,7 @@ def get_id(self, t_now: datetime.datetime) -> str | None: return details.id if details else None def get_aircraft_type(self, rid_version: RIDVersion) -> UAType: - if not self.has_field_with_value("aircraft_type"): + if not self.has_field_with_value("aircraft_type") or not self.aircraft_type: return UAType.NotDeclared # there exists a small difference in the enums between both versions of RID, this ensures we always return the expected one @@ -168,7 +175,10 @@ def get_aircraft_type(self, rid_version: RIDVersion) -> UAType: def order_telemetry(self): self.telemetry = sorted( - self.telemetry, key=lambda telemetry: telemetry.timestamp.datetime + self.telemetry, + key=lambda telemetry: telemetry.timestamp.datetime + if telemetry.timestamp + else 0, ) def select_relevant_states( @@ -179,15 +189,21 @@ def select_relevant_states( previously_inside = False previous_telemetry = None for telemetry in self.telemetry: - if telemetry.timestamp.datetime < t0 or telemetry.timestamp.datetime > t1: + if ( + not telemetry.timestamp + or telemetry.timestamp.datetime < t0 + or telemetry.timestamp.datetime > t1 + ): # Telemetry not relevant based on time continue + if not telemetry.position: + continue pt = s2sphere.LatLng.from_degrees( telemetry.position.lat, telemetry.position.lng ) inside_now = view.contains(pt) if inside_now: - if previously_outside: + if previously_outside and previous_telemetry: recent_states.append(previous_telemetry) recent_states.append(telemetry) previously_inside = True @@ -202,7 +218,11 @@ def select_relevant_states( def get_rect(self) -> s2sphere.LatLngRect | None: return geo.bounding_rect( - [(t.position.lat, t.position.lng) for t in self.telemetry] + [ + (t.position.lat, t.position.lng) + for t in self.telemetry + if t.position and t.position.lat and t.position.lng + ] ) def get_mean_update_rate_hz(self) -> float | None: @@ -213,6 +233,9 @@ def get_mean_update_rate_hz(self) -> float | None: return None # TODO check if required or not (may have been called earlier?) self.order_telemetry() + + if not self.telemetry[0].timestamp or not self.telemetry[-1].timestamp: + return start = self.telemetry[0].timestamp.datetime end = self.telemetry[-1].timestamp.datetime return (len(self.telemetry) - 1) / (end - start).seconds @@ -228,9 +251,9 @@ def get_span( for flight in self.requested_flights: flight = TestFlight(flight) (t0, t1) = flight.get_span() - if earliest is None or t0 < earliest: + if t0 and (earliest is None or t0 < earliest): earliest = t0 - if latest is None or t1 > latest: + if t1 and (latest is None or t1 > latest): latest = t1 return (earliest, latest) diff --git a/monitoring/monitorlib/rid_v1.py b/monitoring/monitorlib/rid_v1.py index a22999576d..5ef0cb8bca 100644 --- a/monitoring/monitorlib/rid_v1.py +++ b/monitoring/monitorlib/rid_v1.py @@ -76,21 +76,17 @@ def flights_url(self) -> str | None: class Flight(dict): @property def valid(self) -> bool: - if self.id is None: - return False - return True + return self.id is not None @property - def id(self) -> str: + def id(self) -> str | None: return self.get("id", None) class Subscription(dict): @property def valid(self) -> bool: - if self.version is None: - return False - return True + return self.version is not None @property def version(self) -> str | None: diff --git a/monitoring/monitorlib/schema_validation.py b/monitoring/monitorlib/schema_validation.py index a355e69879..e96a851dd6 100644 --- a/monitoring/monitorlib/schema_validation.py +++ b/monitoring/monitorlib/schema_validation.py @@ -3,9 +3,9 @@ from enum import Enum from pathlib import Path -import bc_jsonpath_ng import jsonschema.validators import yaml +from bc_jsonpath_ng.parser import parse from implicitdict import ImplicitDict from implicitdict.jsonschema import SchemaVars, make_json_schema @@ -129,7 +129,7 @@ def validate( resolver = jsonschema.validators.RefResolver( base_uri=f"{Path(base_path).as_uri()}/", referrer=openapi_content ) - schema_matches = bc_jsonpath_ng.parse(object_path).find(openapi_content) + schema_matches = parse(object_path).find(openapi_content) if len(schema_matches) != 1: raise ValueError( f"Found {len(schema_matches)} matches to JSON path '{object_path}' within OpenAPI definition at {openapi_path} when expecting exactly 1 match" diff --git a/monitoring/monitorlib/subscription_params.py b/monitoring/monitorlib/subscription_params.py index 65906a36e9..5be3ffbece 100644 --- a/monitoring/monitorlib/subscription_params.py +++ b/monitoring/monitorlib/subscription_params.py @@ -1,7 +1,6 @@ from __future__ import annotations import datetime -from typing import Self import s2sphere from implicitdict import ImplicitDict @@ -47,7 +46,7 @@ class SubscriptionParams(ImplicitDict): notify_for_constraints: bool """Whether to notify for constraints""" - def copy(self) -> Self: + def copy(self) -> SubscriptionParams: return SubscriptionParams(super().copy()) def to_upsert_subscription_params( diff --git a/monitoring/monitorlib/temporal.py b/monitoring/monitorlib/temporal.py index 73273c0295..db87e5a581 100644 --- a/monitoring/monitorlib/temporal.py +++ b/monitoring/monitorlib/temporal.py @@ -144,7 +144,9 @@ def resolve(self, times: dict[TimeDuringTest, Time]) -> Time: # Step linearly through the day looking for two adjacent times that surround the target sun elevation. # Note that this will fail to capture the very peak sun elevation if it is targeted. t2 = t0 + t1 = t2 el2 = _sun_elevation(t2, lat, lng) + el1 = el2 found = False while t2 <= t0 + timedelta(days=1): t1 = t2 @@ -209,7 +211,7 @@ def _sun_elevation(t: datetime, lat_deg: float, lng_deg: float) -> float: Returns: Degrees above the horizon of the center of the sun. """ - return get_solarposition(t, lat_deg, lng_deg).elevation.values[0] + return get_solarposition(t, lat_deg, lng_deg).elevation.values[0] # pyright:ignore[reportAttributeAccessIssue] class Time(StringBasedDateTime):