From f5edb7e273b961c842a5db7601bd682bb0ae1fb9 Mon Sep 17 00:00:00 2001 From: Maximilien Cuony Date: Tue, 19 Aug 2025 11:45:39 +0200 Subject: [PATCH 1/2] [tooling] ruff: enable pyupgrade --- pyproject.toml | 4 ++-- uv.lock | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8f79c60845..4dd971e684 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,9 +55,9 @@ dependencies = [ [tool.ruff] target-version = "py313" -# Default + isort +# Default + isort + pyupgrade lint.select = [ - "E4", "E7", "E9", "F", "I", + "E4", "E7", "E9", "F", "I", "UP" ] extend-exclude = ["interfaces/*"] line-length = 88 diff --git a/uv.lock b/uv.lock index a476c82a4d..d05a13510f 100644 --- a/uv.lock +++ b/uv.lock @@ -566,16 +566,16 @@ wheels = [ [[package]] name = "implicitdict" -version = "3.0.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arrow" }, { name = "jsonschema" }, { name = "pytimeparse" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/83/834fc20d08fc4551868b7062bd266f05d74e17cf1b854f594822cad9dd9b/implicitdict-3.0.0.tar.gz", hash = "sha256:11b9ea6d849a727b8473e9b41e7a21ebb02611c014a2b0c9e1c597c8764e631e", size = 28131, upload-time = "2025-03-05T18:22:51.345Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/35/08b36415cd0da95c347ce9e2ad74440d67108a3a14e47ec39ffb8141bd85/implicitdict-4.0.0.tar.gz", hash = "sha256:d7a799822e6be60884b7a4a91716d1ec805c184040e8b3da3eac6d0d4c012d2e", size = 57839, upload-time = "2025-08-19T09:27:58.197Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/10/396c1f9c22ee9a723f6751090710c32d302d8849c83921ee40a4e9c881ec/implicitdict-3.0.0-py3-none-any.whl", hash = "sha256:c2dc106d148825295c722a9edc6f766fd86cd565923b10fac5015174d3bcb073", size = 17351, upload-time = "2025-03-05T18:22:50.353Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0c/2b5bc3961cc1d0cc279cb7ef29b0dcdba328a935dfa5c9b8a36d5876d3e4/implicitdict-4.0.0-py3-none-any.whl", hash = "sha256:b0450ce897b982a32c8969c5c7a8d6fe0896add536c64a13403c2b6d2f86ef20", size = 13625, upload-time = "2025-08-19T09:27:57.323Z" }, ] [[package]] From 67484ff1b6e21e7f7231b998edc63d4eb95bada8 Mon Sep 17 00:00:00 2001 From: Maximilien Cuony Date: Tue, 19 Aug 2025 11:52:39 +0200 Subject: [PATCH 2/2] [tooling] ruff: apply pyupgrade --- .basedpyright/baseline.json | 184 ++++++++-------- build/dev/extract_json_field.py | 2 +- .../deployment_manager/actions/dss/v1/crdb.py | 12 +- monitoring/deployment_manager/actions/k8s.py | 4 +- .../actions/test/hello_world.py | 8 +- .../deploylib/common_k8s.py | 23 +- .../deploylib/comparisons.py | 13 +- .../deploylib/crdb_cluster_api.py | 35 ++- .../deployment_manager/deploylib/crdb_sql.py | 21 +- .../deploylib/deployments.py | 4 +- .../deployment_manager/deploylib/ingresses.py | 4 +- .../deploylib/namespaces.py | 4 +- .../deploylib/port_forwarding.py | 25 +-- .../deployment_manager/deploylib/secrets.py | 4 +- .../deployment_manager/deploylib/services.py | 4 +- .../deployment_manager/deploylib/systems.py | 23 +- .../deployment_manager/deployment_manager.py | 10 +- .../deployment_manager/infrastructure.py | 18 +- .../systems/configuration.py | 8 +- .../systems/test/configuration.py | 4 +- .../systems/test/hello_world.py | 8 +- monitoring/get_access_token.py | 3 +- monitoring/loadtest/locust_files/ISA.py | 8 +- monitoring/loadtest/locust_files/Sub.py | 8 +- monitoring/loadtest/locust_files/client.py | 7 +- monitoring/mock_uss/__init__.py | 9 +- monitoring/mock_uss/database.py | 13 +- .../mock_uss/dynamic_configuration/routes.py | 6 +- .../mock_uss/f3548v21/flight_planning.py | 38 ++-- monitoring/mock_uss/f3548v21/routes_scd.py | 11 +- monitoring/mock_uss/flight_planning/routes.py | 15 +- monitoring/mock_uss/flights/database.py | 7 +- monitoring/mock_uss/flights/planning.py | 4 +- monitoring/mock_uss/geoawareness/check.py | 7 +- monitoring/mock_uss/geoawareness/database.py | 11 +- monitoring/mock_uss/geoawareness/ed269.py | 23 +- .../geoawareness/routes_geoawareness.py | 14 +- .../routes_interactions_log.py | 9 +- monitoring/mock_uss/logging.py | 11 +- .../mock_uss/msgsigning/routes_msgsigning.py | 2 +- monitoring/mock_uss/riddp/behavior.py | 6 +- monitoring/mock_uss/riddp/clustering.py | 15 +- monitoring/mock_uss/riddp/database.py | 11 +- monitoring/mock_uss/riddp/routes_behavior.py | 8 +- .../mock_uss/riddp/routes_observation.py | 19 +- monitoring/mock_uss/riddp/routes_riddp_v19.py | 4 +- .../mock_uss/riddp/routes_riddp_v22a.py | 4 +- monitoring/mock_uss/ridsp/behavior.py | 10 +- monitoring/mock_uss/ridsp/database.py | 9 +- monitoring/mock_uss/ridsp/routes_behavior.py | 8 +- monitoring/mock_uss/ridsp/routes_injection.py | 11 +- monitoring/mock_uss/ridsp/routes_ridsp_v19.py | 13 +- .../mock_uss/ridsp/routes_ridsp_v22a.py | 15 +- .../mock_uss/ridsp/user_notifications.py | 15 +- monitoring/mock_uss/routes.py | 5 +- .../scd_injection/routes_injection.py | 39 ++-- monitoring/mock_uss/server.py | 24 +-- monitoring/mock_uss/tracer/context.py | 10 +- monitoring/mock_uss/tracer/database.py | 3 +- monitoring/mock_uss/tracer/diff.py | 6 +- monitoring/mock_uss/tracer/kml.py | 48 ++--- monitoring/mock_uss/tracer/log_types.py | 13 +- .../mock_uss/tracer/observation_areas.py | 24 +-- monitoring/mock_uss/tracer/routes/__init__.py | 7 +- .../tracer/routes/observation_areas.py | 13 +- monitoring/mock_uss/tracer/routes/rid.py | 7 +- monitoring/mock_uss/tracer/routes/scd.py | 37 +--- monitoring/mock_uss/tracer/routes/ui.py | 4 +- monitoring/mock_uss/tracer/subscriptions.py | 12 +- monitoring/mock_uss/tracer/template.py | 4 +- monitoring/mock_uss/tracer/tracer_poll.py | 13 +- monitoring/mock_uss/tracer/tracerlog.py | 14 +- monitoring/mock_uss/ui/auth.py | 13 +- monitoring/mock_uss/versioning/routes.py | 4 +- monitoring/monitorlib/auth.py | 142 ++++++------ monitoring/monitorlib/auth_validation.py | 10 +- monitoring/monitorlib/clients/__init__.py | 3 +- .../clients/flight_planning/client.py | 9 +- .../clients/flight_planning/client_scd.py | 11 +- .../clients/flight_planning/client_v1.py | 9 +- .../clients/flight_planning/flight_info.py | 37 ++-- .../flight_planning/flight_info_template.py | 18 +- .../clients/flight_planning/planning.py | 19 +- .../flight_planning/test_preparation.py | 10 +- .../clients/geospatial_info/client.py | 3 +- .../geospatial_info/client_geospatial_map.py | 6 +- .../clients/geospatial_info/querying.py | 17 +- .../clients/mock_uss/interactions.py | 3 +- .../mock_uss/mock_uss_scd_injection_api.py | 8 +- monitoring/monitorlib/clients/scd.py | 16 +- .../monitorlib/clients/versioning/client.py | 5 +- .../clients/versioning/client_interuss.py | 6 +- monitoring/monitorlib/delay.py | 3 +- monitoring/monitorlib/deprecation.py | 11 +- monitoring/monitorlib/dicts.py | 12 +- monitoring/monitorlib/fetch/__init__.py | 70 +++--- monitoring/monitorlib/fetch/evaluation.py | 15 +- monitoring/monitorlib/fetch/rid.py | 174 +++++++-------- monitoring/monitorlib/fetch/scd.py | 83 ++++--- monitoring/monitorlib/fetch/summarize.py | 15 +- monitoring/monitorlib/formatting.py | 11 +- monitoring/monitorlib/geo.py | 74 +++---- monitoring/monitorlib/geo_test.py | 6 +- monitoring/monitorlib/geotemporal.py | 71 +++--- monitoring/monitorlib/idempotency.py | 14 +- monitoring/monitorlib/ids.py | 4 +- monitoring/monitorlib/infrastructure.py | 23 +- monitoring/monitorlib/inspection.py | 7 +- monitoring/monitorlib/kml/f3548v21.py | 4 +- monitoring/monitorlib/kml/flight_planning.py | 4 +- monitoring/monitorlib/kml/generation.py | 11 +- monitoring/monitorlib/kml/parsing.py | 4 +- monitoring/monitorlib/multiprocessing.py | 17 +- monitoring/monitorlib/mutate/rid.py | 71 +++--- monitoring/monitorlib/mutate/scd.py | 23 +- monitoring/monitorlib/rid.py | 36 ++-- .../rid_automated_testing/injection_api.py | 23 +- monitoring/monitorlib/rid_v1.py | 25 ++- monitoring/monitorlib/rid_v2.py | 7 +- monitoring/monitorlib/scd.py | 3 +- monitoring/monitorlib/schema_validation.py | 21 +- monitoring/monitorlib/subscription_params.py | 10 +- monitoring/monitorlib/temporal.py | 17 +- monitoring/monitorlib/testing.py | 5 +- monitoring/monitorlib/transformations.py | 16 +- monitoring/monitorlib/uspace.py | 5 +- monitoring/monitorlib/versioning.py | 9 +- .../prober/aux_/test_token_validation.py | 2 +- monitoring/prober/conftest.py | 16 +- monitoring/prober/decode_id.py | 11 +- monitoring/prober/infrastructure.py | 37 +--- monitoring/prober/scd/actions.py | 20 +- .../prober/scd/test_constraint_simple.py | 74 +++---- .../test_constraints_with_subscriptions.py | 53 +++-- .../test_operation_references_error_cases.py | 132 ++++-------- ...t_operation_references_state_transition.py | 30 +-- .../prober/scd/test_operation_simple.py | 44 ++-- .../test_operation_simple_heavy_traffic.py | 14 +- ...eration_simple_heavy_traffic_concurrent.py | 31 ++- .../scd/test_operation_special_cases.py | 26 +-- .../prober/scd/test_operations_simple.py | 91 +++----- .../prober/scd/test_subscription_queries.py | 12 +- .../scd/test_subscription_query_time.py | 2 +- .../prober/scd/test_subscription_simple.py | 16 +- .../test_subscription_update_validation.py | 26 ++- monitoring/prober/utils.py | 2 +- .../action_generators/action_generator.py | 23 +- .../astm/f3411/for_each_dss.py | 11 +- .../astm/f3548/for_each_dss.py | 11 +- .../action_generators/definitions.py | 4 +- .../documentation/definitions.py | 12 +- .../documentation/documentation.py | 8 +- .../flight_planning/planner_combinations.py | 15 +- .../interuss/mock_uss/with_locality.py | 11 +- .../action_generators/repetition/repeat.py | 11 +- .../uss_qualifier/common_data_definitions.py | 3 +- .../configurations/configuration.py | 86 ++++---- monitoring/uss_qualifier/documentation.py | 2 +- monitoring/uss_qualifier/fileio.py | 25 ++- monitoring/uss_qualifier/main.py | 9 +- .../uss_qualifier/reports/capabilities.py | 21 +- .../reports/capability_definitions.py | 20 +- .../reports/globally_expanded/generate.py | 12 +- monitoring/uss_qualifier/reports/report.py | 203 +++++++++--------- .../reports/sequence_view/events.py | 17 +- .../reports/sequence_view/generate.py | 6 +- .../reports/sequence_view/kml.py | 12 +- .../reports/sequence_view/summary_types.py | 53 +++-- monitoring/uss_qualifier/reports/templates.py | 3 +- .../reports/tested_requirements/breakdown.py | 18 +- .../reports/tested_requirements/data_types.py | 23 +- .../reports/tested_requirements/generate.py | 9 +- .../reports/tested_requirements/sorting.py | 5 +- .../reports/tested_requirements/summaries.py | 10 +- .../reports/validation/definitions.py | 64 +++--- .../reports/validation/report_validation.py | 20 +- .../uss_qualifier/requirements/definitions.py | 9 +- .../requirements/documentation.py | 19 +- .../uss_qualifier/resources/astm/f3411/dss.py | 11 +- .../resources/astm/f3548/v21/dss.py | 87 ++++---- .../resources/communications/auth_adapter.py | 17 +- .../communications/client_identity.py | 2 +- .../uss_qualifier/resources/definitions.py | 6 +- .../uss_qualifier/resources/dev/noop.py | 2 +- .../resources/dev/test_exclusions.py | 8 +- .../eurocae/ed269/source_document.py | 2 +- monitoring/uss_qualifier/resources/files.py | 3 +- .../flight_planning/flight_intent.py | 21 +- .../flight_intent_validation.py | 34 +-- .../flight_intents_resource.py | 6 +- .../flight_planning/flight_planner.py | 7 +- .../flight_planning/flight_planners.py | 22 +- .../geospatial_info_providers.py | 9 +- .../resources/interuss/datastore/datastore.py | 17 +- .../flight_authorization/definitions.py | 5 +- .../flight_check_table.py | 2 +- .../interuss/geospatial_map/definitions.py | 9 +- .../geospatial_map/feature_check_table.py | 2 +- .../resources/interuss/id_generator.py | 2 +- .../resources/interuss/mock_uss/client.py | 24 +-- .../resources/interuss/mock_uss/locality.py | 2 +- .../resources/interuss/query_behavior.py | 14 +- .../resources/interuss/uss_identification.py | 15 +- .../resources/netrid/evaluation.py | 4 +- .../resources/netrid/flight_data.py | 20 +- .../resources/netrid/flight_data_resources.py | 16 +- .../resources/netrid/observers.py | 21 +- .../resources/netrid/service_area.py | 8 +- .../resources/netrid/service_providers.py | 11 +- .../adjacent_circular_flights_simulator.py | 21 +- .../netrid/simulation/kml_flights.py | 3 +- .../resources/netrid/simulation/utils.py | 4 +- .../uss_qualifier/resources/overrides.py | 2 +- .../uss_qualifier/resources/planning_area.py | 13 +- .../uss_qualifier/resources/resource.py | 34 ++- .../resources/versioning/client.py | 10 +- .../resources/versioning/system_identity.py | 2 +- .../uss_qualifier/resources/vertices.py | 6 +- .../scenarios/astm/dss/datastore_access.py | 4 +- .../astm/netrid/common/aggregate_checks.py | 23 +- .../astm/netrid/common/dp_behavior.py | 11 +- .../netrid/common/dss/endpoint_encryption.py | 3 +- .../common/dss/heavy_traffic_concurrent.py | 17 +- .../astm/netrid/common/dss/isa_expiry.py | 3 +- .../astm/netrid/common/dss/isa_simple.py | 5 +- .../dss/isa_subscription_interactions.py | 4 +- .../astm/netrid/common/dss/isa_validation.py | 16 +- .../astm/netrid/common/dss/isa_validator.py | 25 +-- .../netrid/common/dss/subscription_simple.py | 24 +-- .../common/dss/subscription_validation.py | 3 +- .../netrid/common/dss/token_validation.py | 15 +- .../scenarios/astm/netrid/common/dss/utils.py | 6 +- .../netrid/common/dss_interoperability.py | 23 +- .../astm/netrid/common/misbehavior.py | 15 +- .../netrid/common/networked_uas_disconnect.py | 5 +- .../astm/netrid/common/nominal_behavior.py | 8 +- .../netrid/common/sp_notification_behavior.py | 27 +-- .../sp_operator_notify_missing_fields.py | 6 +- .../common/sp_operator_notify_slow_update.py | 7 +- .../netrid/common_dictionary_evaluator.py | 152 ++++++------- .../common_dictionary_evaluator_test.py | 61 +++--- .../astm/netrid/display_data_evaluator.py | 93 ++++---- .../netrid/display_data_evaluator_test.py | 48 ++--- .../scenarios/astm/netrid/dss_wrapper.py | 84 ++++---- .../astm/netrid/injected_flight_collection.py | 7 +- .../scenarios/astm/netrid/injection.py | 13 +- .../astm/netrid/store_flight_data.py | 6 +- .../astm/netrid/v19/aggregate_checks.py | 4 +- .../astm/netrid/v22a/aggregate_checks.py | 4 +- .../scenarios/astm/netrid/virtual_observer.py | 8 +- .../scenarios/astm/utm/aggregate_checks.py | 10 +- .../astm/utm/clear_area_validation.py | 6 +- .../get_op_data_validation.py | 10 +- .../expected_interactions_test_steps.py | 3 +- .../test_steps/wait.py | 6 +- .../authentication_validation.py | 8 +- .../availability_api_validator.py | 6 +- .../dss/authentication/cr_api_validator.py | 3 +- .../dss/authentication/oir_api_validator.py | 3 +- .../dss/authentication/sub_api_validator.py | 3 +- .../astm/utm/dss/dss_interoperability.py | 7 +- .../astm/utm/dss/fragments/oir/__init__.py | 8 +- .../utm/dss/fragments/oir/crud/__init__.py | 14 +- .../utm/dss/fragments/sub/crud/__init__.py | 8 +- .../astm/utm/dss/oir_explicit_sub_handling.py | 11 +- .../astm/utm/dss/oir_implicit_sub_handling.py | 23 +- .../utm/dss/op_intent_ref_access_control.py | 16 +- .../utm/dss/op_intent_ref_key_validation.py | 13 +- .../astm/utm/dss/op_intent_ref_simple.py | 7 +- .../dss/op_intent_ref_state_transitions.py | 16 +- .../astm/utm/dss/subscription_interactions.py | 15 +- .../dss/subscription_interactions_deletion.py | 9 +- .../astm/utm/dss/subscription_simple.py | 11 +- .../astm/utm/dss/subscription_validation.py | 4 +- .../constraint_ref_synchronization.py | 9 +- .../op_intent_ref_synchronization.py | 9 +- .../subscription_synchronization.py | 19 +- .../uss_availability_synchronization.py | 6 +- .../astm/utm/dss/test_step_fragments.py | 4 +- .../astm/utm/dss/validators/__init__.py | 3 +- .../astm/utm/dss/validators/cr_validator.py | 17 +- .../astm/utm/dss/validators/oir_validator.py | 17 +- .../dss/validators/subscription_validator.py | 13 +- .../scenarios/astm/utm/evaluation.py | 25 +-- .../flight_intent_validation.py | 3 +- .../scenarios/astm/utm/make_uss_report.py | 6 +- .../assets/make_assets.py | 3 +- .../conflict_equal_priority_not_permitted.py | 18 +- .../assets/make_assets.py | 3 +- .../conflict_higher_priority.py | 18 +- .../utm/nominal_planning/solo_happy_path.py | 8 +- .../astm/utm/off_nominal_planning/down_uss.py | 8 +- .../down_uss_equal_priority_not_permitted.py | 4 +- .../scenarios/astm/utm/prep_planners.py | 14 +- .../assets/make_assets.py | 3 +- .../receive_notifications_for_awareness.py | 8 +- .../scenarios/astm/utm/test_steps.py | 43 ++-- .../versioning/evaluate_system_versions.py | 17 +- .../uss_qualifier/scenarios/definitions.py | 6 +- .../scenarios/documentation/autoformat.py | 10 +- .../scenarios/documentation/definitions.py | 28 ++- .../scenarios/documentation/parsing.py | 44 ++-- .../scenarios/documentation/requirements.py | 26 ++- .../scenarios/documentation/validation.py | 3 +- .../eurocae/ed269/source_data_model.py | 3 +- .../flight_planning/prep_planners.py | 13 +- .../prioritization_test_steps.py | 22 +- .../scenarios/flight_planning/test_steps.py | 34 +-- .../general_flight_authorization.py | 4 +- .../geospatial_feature_comprehension.py | 4 +- .../interuss/mock_uss/configure_locality.py | 8 +- .../scenarios/interuss/mock_uss/test_steps.py | 8 +- .../interuss/mock_uss/unconfigure_locality.py | 5 +- .../interuss/ovn_request/dss_ovn_request.py | 7 +- .../scenarios/interuss/unit_test.py | 2 +- .../uss_qualifier/scenarios/scenario.py | 57 +++-- .../scenarios/scenario_test/utils.py | 10 +- .../uspace/flight_auth/validation.py | 6 +- .../scenarios/uspace/netrid/msl.py | 6 +- .../versioning/get_system_versions.py | 2 +- .../uss_qualifier/scripts/report_analyzer.py | 2 +- monitoring/uss_qualifier/signatures.py | 6 +- .../uss_qualifier/suites/definitions.py | 25 ++- .../suites/documentation/documentation.py | 62 +++--- .../documentation/format_documentation.py | 2 +- monitoring/uss_qualifier/suites/suite.py | 68 +++--- monitoring/uss_qualifier/validation.py | 4 +- monitoring/validate_access_token.py | 3 +- schemas/manage_type_schemas.py | 24 +-- test/repo_hygiene/md_files/md_files.py | 2 +- 330 files changed, 2677 insertions(+), 3263 deletions(-) mode change 100644 => 100755 monitoring/uss_qualifier/reports/tested_requirements/sorting.py diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 61bc42826b..d3d07b656f 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -228,24 +228,24 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 34, - "endColumn": 36, + "startColumn": 44, + "endColumn": 46, "lineCount": 1 } }, { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 37, - "endColumn": 46, + "startColumn": 47, + "endColumn": 56, "lineCount": 1 } }, { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 69, - "endColumn": 73, + "startColumn": 100, + "endColumn": 104, "lineCount": 1 } }, @@ -268,16 +268,16 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 31, - "endColumn": 35, + "startColumn": 64, + "endColumn": 68, "lineCount": 1 } }, { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 58, - "endColumn": 62, + "startColumn": 107, + "endColumn": 111, "lineCount": 1 } }, @@ -670,8 +670,8 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 39, - "endColumn": 43, + "startColumn": 129, + "endColumn": 133, "lineCount": 1 } }, @@ -3080,8 +3080,8 @@ { "code": "reportArgumentType", "range": { - "startColumn": 54, - "endColumn": 74, + "startColumn": 67, + "endColumn": 87, "lineCount": 1 } } @@ -3646,32 +3646,32 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 19, + "endColumn": 22, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 30, - "endColumn": 33, + "startColumn": 21, + "endColumn": 24, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 19, - "endColumn": 22, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 55, - "endColumn": 58, + "startColumn": 50, + "endColumn": 53, "lineCount": 1 } }, @@ -3822,8 +3822,8 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 24, - "endColumn": 27, + "startColumn": 15, + "endColumn": 18, "lineCount": 1 } }, @@ -3846,32 +3846,32 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 19, + "endColumn": 22, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 30, - "endColumn": 33, + "startColumn": 21, + "endColumn": 24, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 19, - "endColumn": 22, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 39, - "endColumn": 42, + "startColumn": 34, + "endColumn": 37, "lineCount": 1 } }, @@ -4134,16 +4134,16 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 52, - "endColumn": 55, + "startColumn": 38, + "endColumn": 41, "lineCount": 1 } }, @@ -4214,16 +4214,16 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 54, - "endColumn": 57, + "startColumn": 40, + "endColumn": 43, "lineCount": 1 } }, @@ -4262,16 +4262,16 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 48, - "endColumn": 51, + "startColumn": 34, + "endColumn": 37, "lineCount": 1 } }, @@ -4350,32 +4350,32 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 19, + "endColumn": 22, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 30, - "endColumn": 33, + "startColumn": 21, + "endColumn": 24, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 19, - "endColumn": 22, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 46, - "endColumn": 49, + "startColumn": 41, + "endColumn": 44, "lineCount": 1 } }, @@ -4422,8 +4422,8 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 23, - "endColumn": 26, + "startColumn": 14, + "endColumn": 17, "lineCount": 1 } }, @@ -4598,32 +4598,32 @@ { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 28, - "endColumn": 31, + "startColumn": 19, + "endColumn": 22, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 30, - "endColumn": 33, + "startColumn": 21, + "endColumn": 24, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 19, - "endColumn": 22, + "startColumn": 13, + "endColumn": 16, "lineCount": 1 } }, { "code": "reportAttributeAccessIssue", "range": { - "startColumn": 42, - "endColumn": 45, + "startColumn": 37, + "endColumn": 40, "lineCount": 1 } }, @@ -7314,8 +7314,8 @@ { "code": "reportCallIssue", "range": { - "startColumn": 67, - "endColumn": 72, + "startColumn": 59, + "endColumn": 64, "lineCount": 1 } }, @@ -7330,8 +7330,8 @@ { "code": "reportCallIssue", "range": { - "startColumn": 63, - "endColumn": 68, + "startColumn": 55, + "endColumn": 60, "lineCount": 1 } }, @@ -7812,8 +7812,8 @@ { "code": "reportInvalidTypeVarUse", "range": { - "startColumn": 51, - "endColumn": 72, + "startColumn": 20, + "endColumn": 41, "lineCount": 1 } }, @@ -10888,16 +10888,16 @@ { "code": "reportGeneralTypeIssues", "range": { - "startColumn": 36, - "endColumn": 39, + "startColumn": 27, + "endColumn": 30, "lineCount": 1 } }, { "code": "reportGeneralTypeIssues", "range": { - "startColumn": 39, - "endColumn": 42, + "startColumn": 30, + "endColumn": 33, "lineCount": 1 } }, @@ -12279,14 +12279,6 @@ "lineCount": 1 } }, - { - "code": "reportInvalidTypeArguments", - "range": { - "startColumn": 37, - "endColumn": 86, - "lineCount": 1 - } - }, { "code": "reportOptionalMemberAccess", "range": { @@ -12704,8 +12696,8 @@ { "code": "reportInvalidTypeVarUse", "range": { - "startColumn": 48, - "endColumn": 49, + "startColumn": 39, + "endColumn": 40, "lineCount": 1 } } @@ -22912,8 +22904,8 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 68, - "endColumn": 73, + "startColumn": 192, + "endColumn": 197, "lineCount": 1 } }, @@ -22928,8 +22920,8 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 68, - "endColumn": 73, + "startColumn": 192, + "endColumn": 197, "lineCount": 1 } }, @@ -22944,8 +22936,8 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 67, - "endColumn": 72, + "startColumn": 174, + "endColumn": 179, "lineCount": 1 } }, @@ -22960,8 +22952,8 @@ { "code": "reportOptionalMemberAccess", "range": { - "startColumn": 63, - "endColumn": 68, + "startColumn": 170, + "endColumn": 175, "lineCount": 1 } } @@ -25808,8 +25800,8 @@ { "code": "reportGeneralTypeIssues", "range": { - "startColumn": 31, - "endColumn": 50, + "startColumn": 22, + "endColumn": 41, "lineCount": 1 } }, @@ -25969,6 +25961,14 @@ } ], "./schemas/manage_type_schemas.py": [ + { + "code": "reportArgumentType", + "range": { + "startColumn": 73, + "endColumn": 77, + "lineCount": 1 + } + }, { "code": "reportAttributeAccessIssue", "range": { diff --git a/build/dev/extract_json_field.py b/build/dev/extract_json_field.py index 024350d67d..3a14b07e43 100644 --- a/build/dev/extract_json_field.py +++ b/build/dev/extract_json_field.py @@ -3,7 +3,7 @@ import json import sys -with open(sys.argv[2], "r") as f: +with open(sys.argv[2]) as f: try: obj = json.load(f) except ValueError as e: diff --git a/monitoring/deployment_manager/actions/dss/v1/crdb.py b/monitoring/deployment_manager/actions/dss/v1/crdb.py index 1169d78e34..33a4c77394 100644 --- a/monitoring/deployment_manager/actions/dss/v1/crdb.py +++ b/monitoring/deployment_manager/actions/dss/v1/crdb.py @@ -57,9 +57,7 @@ def print_ca_public_certs(context: Context): for i in range(len(certs)) if public_key_bytes == _public_key_bytes(certs[i].public_key()) ] - match_words = ["first", "second", "third"] + [ - "{}th".format(i) for i in range(4, 50) - ] + match_words = ["first", "second", "third"] + [f"{i}th" for i in range(4, 50)] if not matches: context.log.warn( "This DSS instance's public key does not appear in any of the listed certificates" @@ -86,7 +84,7 @@ def crdb_status(context: Context): ) cluster = ClusterAPI( pod_session, - base_url="https://{}/api/v2".format(host_port), + base_url=f"https://{host_port}/api/v2", username=username, password=password, ) @@ -101,9 +99,9 @@ def crdb_status(context: Context): for n in nodes: k, v = n.summarize() summary[k] = v - context.log.msg("{} reports:\n".format(source) + yaml.dump(summary)) + context.log.msg(f"{source} reports:\n" + yaml.dump(summary)) else: - context.log.msg("{} not ready to query nodes".format(source)) + context.log.msg(f"{source} not ready to query nodes") @deployment_action("dss/crdb/print_monitoring_user") @@ -119,5 +117,5 @@ def print_monitoring_user(context: Context): username, password = crdb_sql.get_monitoring_user( context.clients.core, context.spec.dss.v1.namespace, context.spec.cluster.name ) - context.log.msg("Username: {} Password: {}".format(username, password)) + context.log.msg(f"Username: {username} Password: {password}") return diff --git a/monitoring/deployment_manager/actions/k8s.py b/monitoring/deployment_manager/actions/k8s.py index c96951133c..f89addd2b9 100644 --- a/monitoring/deployment_manager/actions/k8s.py +++ b/monitoring/deployment_manager/actions/k8s.py @@ -7,7 +7,7 @@ def list_pods(context: Context): ret = context.clients.core.list_pod_for_all_namespaces(watch=False) msg = "\n".join( [ - "{}\t{}\t{}".format(i.status.pod_ip, i.metadata.namespace, i.metadata.name) + f"{i.status.pod_ip}\t{i.metadata.namespace}\t{i.metadata.name}" for i in ret.items ] ) @@ -20,7 +20,7 @@ def list_ingress_controllers(context: Context): class_list = context.clients.networking.list_ingress_class() msg = "\n".join( [ - "{}\t{}\t{}".format(c.metadata.name, c.spec.controller, c.spec.parameters) + f"{c.metadata.name}\t{c.spec.controller}\t{c.spec.parameters}" for c in class_list.items ] ) diff --git a/monitoring/deployment_manager/actions/test/hello_world.py b/monitoring/deployment_manager/actions/test/hello_world.py index ab80186904..768f10d1f4 100644 --- a/monitoring/deployment_manager/actions/test/hello_world.py +++ b/monitoring/deployment_manager/actions/test/hello_world.py @@ -28,9 +28,7 @@ def destroy(context: Context) -> None: ) if namespace is None: context.log.warn( - "Namespace `{}` does not exist in `{}` cluster".format( - context.spec.test.v1.namespace, context.spec.cluster.name - ) + f"Namespace `{context.spec.test.v1.namespace}` does not exist in `{context.spec.cluster.name}` cluster" ) return @@ -41,9 +39,7 @@ def destroy(context: Context) -> None: ) context.log.warn( - "Destroying hello_world system in `{}` namespace of `{}` cluster in 15 seconds...".format( - namespace.metadata.name, context.spec.cluster.name - ) + f"Destroying hello_world system in `{namespace.metadata.name}` namespace of `{context.spec.cluster.name}` cluster in 15 seconds..." ) sleep(15, "destruction of hello_world system may take a few seconds") diff --git a/monitoring/deployment_manager/deploylib/common_k8s.py b/monitoring/deployment_manager/deploylib/common_k8s.py index 53401b61f1..023de18acb 100644 --- a/monitoring/deployment_manager/deploylib/common_k8s.py +++ b/monitoring/deployment_manager/deploylib/common_k8s.py @@ -1,4 +1,5 @@ -from typing import Any, Callable, Optional +from collections.abc import Callable +from typing import Any from structlog import BoundLogger @@ -10,17 +11,15 @@ def get_resource( log: BoundLogger, resource_type: str, resource_name: str, -) -> Optional[Any]: - log.msg("Checking for existing {}".format(resource_type), name=resource_name) +) -> Any | None: + log.msg(f"Checking for existing {resource_type}", name=resource_name) resource_list = list_resources() matching_resources = [ d for d in resource_list.items if d.metadata.name == resource_name ] if len(matching_resources) > 2: raise ValueError( - "Found {} {}s matching `{}`".format( - len(matching_resources), resource_type, resource_name - ) + f"Found {len(matching_resources)} {resource_type}s matching `{resource_name}`" ) if not matching_resources: return None @@ -28,7 +27,7 @@ def get_resource( def upsert_resource( - existing_resource: Optional[Any], + existing_resource: Any | None, target_resource: Any, log: BoundLogger, resource_type: str, @@ -38,16 +37,16 @@ def upsert_resource( if existing_resource is not None: if comparisons.specs_are_the_same(existing_resource, target_resource): log.msg( - "Existing {} does not need to be updated".format(resource_type), + f"Existing {resource_type} does not need to be updated", name=existing_resource.metadata.name, ) new_resource = existing_resource else: - log.msg("Updating existing {}".format(resource_type)) + log.msg(f"Updating existing {resource_type}") new_resource = patch() - log.msg("Updated {}".format(resource_type), name=new_resource.metadata.name) + log.msg(f"Updated {resource_type}", name=new_resource.metadata.name) else: - log.msg("Creating new {}".format(resource_type)) + log.msg(f"Creating new {resource_type}") new_resource = create() - log.msg("Created {}".format(resource_type), name=new_resource.metadata.name) + log.msg(f"Created {resource_type}", name=new_resource.metadata.name) return new_resource diff --git a/monitoring/deployment_manager/deploylib/comparisons.py b/monitoring/deployment_manager/deploylib/comparisons.py index 731df72237..268d6086cc 100644 --- a/monitoring/deployment_manager/deploylib/comparisons.py +++ b/monitoring/deployment_manager/deploylib/comparisons.py @@ -1,4 +1,5 @@ -from typing import Any, Callable, Dict, List, Optional, Type +from collections.abc import Callable +from typing import Any from kubernetes.client import ( V1Deployment, @@ -9,11 +10,11 @@ V1Service, ) -_special_comparisons: Dict[Type, Callable[[Any, Any], bool]] = {} +_special_comparisons: dict[type, Callable[[Any, Any], bool]] = {} def specs_are_the_same( - obj1: Any, obj2: Any, field_paths: Optional[List[str]] = None + obj1: Any, obj2: Any, field_paths: list[str] | None = None ) -> bool: """Determine if the specifications for two Kubernetes objects are equivalent @@ -48,13 +49,13 @@ def specs_are_the_same( else: return obj1 == obj2 - sub_paths: Dict[str, Optional[List[str]]] = {} + sub_paths: dict[str, list[str] | None] = {} for field_path in field_paths: parts = field_path.split(".") if len(parts) == 1: if parts[0] in sub_paths: raise ValueError( - "Cannot compare {} and its subfield {}".format(parts[0], field_path) + f"Cannot compare {parts[0]} and its subfield {field_path}" ) sub_paths[parts[0]] = None else: @@ -76,7 +77,7 @@ def specs_are_the_same( return True -def _special_comparison(type: Type): +def _special_comparison(type: type): def decorator_declare_comparison(compare: Callable[[Any, Any], bool]): global _special_comparisons _special_comparisons[type] = compare diff --git a/monitoring/deployment_manager/deploylib/crdb_cluster_api.py b/monitoring/deployment_manager/deploylib/crdb_cluster_api.py index 2f7e09dac3..a9bd32bd64 100644 --- a/monitoring/deployment_manager/deploylib/crdb_cluster_api.py +++ b/monitoring/deployment_manager/deploylib/crdb_cluster_api.py @@ -1,5 +1,4 @@ import math -from typing import Dict, List, Optional, Tuple import arrow import requests @@ -19,7 +18,7 @@ class ServerVersion(ImplicitDict): class Locality(ImplicitDict): - tiers: List[dict] + tiers: list[dict] class Node(ImplicitDict): @@ -37,8 +36,8 @@ class Node(ImplicitDict): updated_at: int liveness_status: int - def summarize(self) -> Tuple[str, Dict[str, str]]: - key = "Node {} ({})".format(self.node_id, self.address.address_field) + def summarize(self) -> tuple[str, dict[str, str]]: + key = f"Node {self.node_id} ({self.address.address_field})" t0 = arrow.get(math.floor(self.started_at / 1e9)) values = { "locality": " ".join( @@ -51,7 +50,7 @@ def summarize(self) -> Tuple[str, Dict[str, str]]: return key, values -class ClusterAPI(object): +class ClusterAPI: """Wrapper for retrieving CockroachDB cluster information. API: https://www.cockroachlabs.com/docs/api/cluster/v2 @@ -61,8 +60,8 @@ def __init__( self, session: requests.Session, base_url: str = "https://localhost:8080/api/v2", - username: Optional[str] = None, - password: Optional[str] = None, + username: str | None = None, + password: str | None = None, ): self._session = session self._base_url = base_url @@ -74,7 +73,7 @@ def __del__(self): self.log_out() def is_ready(self) -> bool: - resp = self._session.get("{}/health/?ready=true".format(self._base_url)) + resp = self._session.get(f"{self._base_url}/health/?ready=true") if resp.status_code == 200: return True elif resp.status_code == 500: @@ -87,7 +86,7 @@ def is_ready(self) -> bool: ) def is_up(self) -> bool: - resp = self._session.get("{}/health/".format(self._base_url)) + resp = self._session.get(f"{self._base_url}/health/") if resp.status_code == 200: return True elif resp.status_code == 500: @@ -102,18 +101,14 @@ def is_up(self) -> bool: def log_in(self) -> str: if self._username is None: raise ValueError( - "Cannot log in to CockroachDB cluster at {} when username is not specified".format( - self._base_url - ) + f"Cannot log in to CockroachDB cluster at {self._base_url} when username is not specified" ) if self._password is None: raise ValueError( - "Cannot log in to CockroachDB cluster at {} when password is not specified".format( - self._base_url - ) + f"Cannot log in to CockroachDB cluster at {self._base_url} when password is not specified" ) resp = self._session.post( - "{}/login/".format(self._base_url), + f"{self._base_url}/login/", data={"username": self._username, "password": self._password}, ) resp.raise_for_status() @@ -127,7 +122,7 @@ def log_in(self) -> str: self._session_auth = session return session - def _get_headers(self) -> Dict[str, str]: + def _get_headers(self) -> dict[str, str]: if self._session_auth is None: self.log_in() return {"X-Cockroach-API-Session": self._session_auth} @@ -136,14 +131,14 @@ def log_out(self) -> None: if self._session_auth is None: return resp = self._session.post( - "{}/logout/".format(self._base_url), headers=self._get_headers() + f"{self._base_url}/logout/", headers=self._get_headers() ) resp.raise_for_status() self._session_auth = None - def get_nodes(self) -> List[Node]: + def get_nodes(self) -> list[Node]: resp = self._session.get( - "{}/nodes/".format(self._base_url), headers=self._get_headers() + f"{self._base_url}/nodes/", headers=self._get_headers() ) resp.raise_for_status() nodes = resp.json().get("nodes", None) diff --git a/monitoring/deployment_manager/deploylib/crdb_sql.py b/monitoring/deployment_manager/deploylib/crdb_sql.py index 06d47311b8..65c9b1758c 100644 --- a/monitoring/deployment_manager/deploylib/crdb_sql.py +++ b/monitoring/deployment_manager/deploylib/crdb_sql.py @@ -2,20 +2,19 @@ import hashlib import random from dataclasses import dataclass -from typing import List, Tuple import kubernetes.stream from kubernetes import client as k8s @dataclass -class User(object): +class User: username: str - options: List[str] - member_of: List[str] + options: list[str] + member_of: list[str] -def execute_sql(client: k8s.CoreV1Api, namespace: str, sql_commands: List[str]) -> str: +def execute_sql(client: k8s.CoreV1Api, namespace: str, sql_commands: list[str]) -> str: """Execute the specfied sql_commands directly on a CRDB node. :param client: Kubernetes core client which can access the CRDB node @@ -40,7 +39,7 @@ def execute_sql(client: k8s.CoreV1Api, namespace: str, sql_commands: List[str]) return resp -def list_users(client: k8s.CoreV1Api, namespace: str) -> List[User]: +def list_users(client: k8s.CoreV1Api, namespace: str) -> list[User]: lines = execute_sql(client, namespace, ["SHOW USERS"]).split("\n") lines = [line for line in lines[1:] if line] users = [] @@ -56,7 +55,7 @@ def list_users(client: k8s.CoreV1Api, namespace: str) -> List[User]: def get_monitoring_user( client: k8s.CoreV1Api, namespace: str, cluster_name: str -) -> Tuple[str, str]: +) -> tuple[str, str]: """Get the username and password for a CRDB user intended for monitoring. Whenever this routine is called, it sets the validity of the user to 1-2 @@ -92,11 +91,9 @@ def get_monitoring_user( client, namespace, [ - "CREATE USER IF NOT EXISTS {}".format(username), - "GRANT admin TO {}".format(username), - "ALTER USER {} WITH LOGIN PASSWORD '{}' VALID UNTIL '{}'".format( - username, password, valid_until - ), + f"CREATE USER IF NOT EXISTS {username}", + f"GRANT admin TO {username}", + f"ALTER USER {username} WITH LOGIN PASSWORD '{password}' VALID UNTIL '{valid_until}'", ], ) diff --git a/monitoring/deployment_manager/deploylib/deployments.py b/monitoring/deployment_manager/deploylib/deployments.py index b1cc6d0439..883338a1f3 100644 --- a/monitoring/deployment_manager/deploylib/deployments.py +++ b/monitoring/deployment_manager/deploylib/deployments.py @@ -1,5 +1,3 @@ -from typing import Optional - from kubernetes.client import AppsV1Api, V1Deployment, V1Namespace from structlog import BoundLogger @@ -8,7 +6,7 @@ def get( client: AppsV1Api, log: BoundLogger, namespace: V1Namespace, dep: V1Deployment -) -> Optional[V1Deployment]: +) -> V1Deployment | None: return common_k8s.get_resource( lambda: client.list_namespaced_deployment(namespace=namespace.metadata.name), log, diff --git a/monitoring/deployment_manager/deploylib/ingresses.py b/monitoring/deployment_manager/deploylib/ingresses.py index c833f65524..8022586bf0 100644 --- a/monitoring/deployment_manager/deploylib/ingresses.py +++ b/monitoring/deployment_manager/deploylib/ingresses.py @@ -1,5 +1,3 @@ -from typing import Optional - from kubernetes.client import NetworkingV1Api, V1Ingress, V1Namespace from structlog import BoundLogger @@ -11,7 +9,7 @@ def get( log: BoundLogger, namespace: V1Namespace, ingress: V1Ingress, -) -> Optional[V1Ingress]: +) -> V1Ingress | None: return common_k8s.get_resource( lambda: client.list_namespaced_ingress(namespace=namespace.metadata.name), log, diff --git a/monitoring/deployment_manager/deploylib/namespaces.py b/monitoring/deployment_manager/deploylib/namespaces.py index 3dbb5fbd50..b52b074673 100644 --- a/monitoring/deployment_manager/deploylib/namespaces.py +++ b/monitoring/deployment_manager/deploylib/namespaces.py @@ -1,5 +1,3 @@ -from typing import Optional - from kubernetes.client import CoreV1Api, V1Namespace from structlog import BoundLogger @@ -8,7 +6,7 @@ def get( client: CoreV1Api, log: BoundLogger, namespace: V1Namespace -) -> Optional[V1Namespace]: +) -> V1Namespace | None: return common_k8s.get_resource( lambda: client.list_namespace(), log, "namespace", namespace.metadata.name ) diff --git a/monitoring/deployment_manager/deploylib/port_forwarding.py b/monitoring/deployment_manager/deploylib/port_forwarding.py index 3c96fa52a2..baffbd642a 100644 --- a/monitoring/deployment_manager/deploylib/port_forwarding.py +++ b/monitoring/deployment_manager/deploylib/port_forwarding.py @@ -1,5 +1,4 @@ from http.client import HTTPConnection -from typing import Tuple import kubernetes.stream import requests @@ -24,7 +23,7 @@ def _get_session_for_socket(sock, host: str, port: int) -> requests.Session: def get_requests_session_for_pod( pod_name: str, namespace: str, port: int, client: CoreV1Api -) -> Tuple[requests.Session, str]: +) -> tuple[requests.Session, str]: """Make a Session to connect to the specified port on the named pod. Retrieves a requests Session that will send http(s) queries to the specified @@ -45,10 +44,8 @@ def get_requests_session_for_pod( namespace, ports=str(port), ) - host = "{}.pod.{}".format(pod_name, namespace) - return _get_session_for_socket(pf.socket(port), host, port), "{}:{}".format( - host, port - ) + host = f"{pod_name}.pod.{namespace}" + return _get_session_for_socket(pf.socket(port), host, port), f"{host}:{port}" # ==== Custom requests handlers to inject/use an explicitly-provided socket ==== @@ -59,7 +56,7 @@ def __init__(self, sock, host: str, port: int, *args, **kwargs): self._socket = sock self._host = host self._port = port - super(SocketHTTPAdapter, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def init_poolmanager( self, @@ -79,7 +76,7 @@ def __init__(self, sock, host: str, port: int, *args, **kwargs): self._socket = sock self._host = host self._port = port - super(SocketPoolManager, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _new_pool(self, scheme, host, port, request_context=None): """Overrides method in base class""" @@ -94,16 +91,14 @@ def _new_pool(self, scheme, host, port, request_context=None): self._socket, host, port, **self.connection_pool_kw ) raise ValueError( - "{}:{} is not supported by SocketPoolManager intended for {}:{}".format( - host, port, self._host, self._port - ) + f"{host}:{port} is not supported by SocketPoolManager intended for {self._host}:{self._port}" ) class SocketHTTPConnectionPool(HTTPConnectionPool): def __init__(self, sock, *args, **kwargs): self._socket = sock - super(SocketHTTPConnectionPool, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _new_conn(self): """Overrides method in base class""" @@ -120,7 +115,7 @@ def _new_conn(self): class SocketHTTPConnection(HTTPConnection): def __init__(self, sock, *args, **kwargs): self._socket = sock - super(SocketHTTPConnection, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def connect(self): """Overrides method in base class""" @@ -133,7 +128,7 @@ class SocketHTTPSConnectionPool(HTTPSConnectionPool): def __init__(self, sock, *args, **kwargs): self._socket = sock kwargs["assert_hostname"] = False - super(SocketHTTPSConnectionPool, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _new_conn(self): """Overrides method in base class""" @@ -161,7 +156,7 @@ def _new_conn(self): class SocketHTTPSConnection(urllib3.connection.HTTPSConnection): def __init__(self, sock, *args, **kwargs): self._socket = sock - super(SocketHTTPSConnection, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _new_conn(self): """Overrides method in base class""" diff --git a/monitoring/deployment_manager/deploylib/secrets.py b/monitoring/deployment_manager/deploylib/secrets.py index b5f5d1d7a4..08de907b47 100644 --- a/monitoring/deployment_manager/deploylib/secrets.py +++ b/monitoring/deployment_manager/deploylib/secrets.py @@ -1,5 +1,3 @@ -from typing import Optional - from kubernetes.client import CoreV1Api, V1Namespace, V1Secret from structlog import BoundLogger @@ -8,7 +6,7 @@ def get( client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, name: str -) -> Optional[V1Secret]: +) -> V1Secret | None: return common_k8s.get_resource( lambda: client.list_namespaced_secret(namespace=namespace.metadata.name), log, diff --git a/monitoring/deployment_manager/deploylib/services.py b/monitoring/deployment_manager/deploylib/services.py index 2b895202b0..4f4e689c43 100644 --- a/monitoring/deployment_manager/deploylib/services.py +++ b/monitoring/deployment_manager/deploylib/services.py @@ -1,5 +1,3 @@ -from typing import Optional - from kubernetes.client import CoreV1Api, V1Namespace, V1Service from structlog import BoundLogger @@ -8,7 +6,7 @@ def get( client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, svc: V1Service -) -> Optional[V1Service]: +) -> V1Service | None: return common_k8s.get_resource( lambda: client.list_namespaced_service(namespace=namespace.metadata.name), log, diff --git a/monitoring/deployment_manager/deploylib/systems.py b/monitoring/deployment_manager/deploylib/systems.py index b0cc7bf208..bb56c27be0 100644 --- a/monitoring/deployment_manager/deploylib/systems.py +++ b/monitoring/deployment_manager/deploylib/systems.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from kubernetes.client import V1Deployment, V1Ingress, V1Namespace, V1Service from structlog import BoundLogger @@ -13,7 +13,7 @@ def upsert_resources( - target_resources: List[Any], + target_resources: list[Any], namespace: V1Namespace, clients: Clients, log: BoundLogger, @@ -29,17 +29,17 @@ def upsert_resources( services.upsert(clients.core, log, namespace, target_resource) else: raise NotImplementedError( - "Upserting {} is not yet supported".format(target_resource.__class__) + f"Upserting {target_resource.__class__} is not yet supported" ) def get_resources( - target_resources: List[Any], + target_resources: list[Any], namespace: V1Namespace, clients: Clients, log: BoundLogger, cluster_name: str, -) -> List[Any]: +) -> list[Any]: existing_resources = [] for target_resource in target_resources: if target_resource.__class__ == V1Deployment: @@ -58,24 +58,19 @@ def get_resources( ) else: raise NotImplementedError( - "Getting {} is not yet supported".format(target_resource.__class__) + f"Getting {target_resource.__class__} is not yet supported" ) if existing_resource is None: log.warn( - "No existing {} {} found in `{}` namespace of `{}` cluster".format( - target_resource.metadata.name, - target_resource.__class__.__name__, - namespace.metadata.name, - cluster_name, - ) + f"No existing {target_resource.metadata.name} {target_resource.__class__.__name__} found in `{namespace.metadata.name}` namespace of `{cluster_name}` cluster" ) existing_resources.append(existing_resource) return existing_resources def delete_resources( - existing_resources: List[Any], + existing_resources: list[Any], namespace: V1Namespace, clients: Clients, log: BoundLogger, @@ -107,5 +102,5 @@ def delete_resources( log.msg("Service deleted", message=svc.metadata.name) else: raise NotImplementedError( - "Deleting {} is not yet supported".format(existing_resource.__class__) + f"Deleting {existing_resource.__class__} is not yet supported" ) diff --git a/monitoring/deployment_manager/deployment_manager.py b/monitoring/deployment_manager/deployment_manager.py index 38fdaef8ee..4593fba333 100755 --- a/monitoring/deployment_manager/deployment_manager.py +++ b/monitoring/deployment_manager/deployment_manager.py @@ -39,12 +39,10 @@ def main() -> int: # Retrieve action function action_method = infrastructure.actions.get(args.action, None) if action_method is None: - raise ValueError( - "Could not find definition for action `{}`".format(args.action) - ) + raise ValueError(f"Could not find definition for action `{args.action}`") # Parse deployment spec - with open(args.deployment_spec, "r") as f: + with open(args.deployment_spec) as f: spec = ImplicitDict.parse(json.load(f), DeploymentSpec) original_spec = json.dumps(spec) context = make_context(spec) @@ -59,9 +57,7 @@ def main() -> int: new_spec = json.dumps(context.spec) if new_spec != original_spec: context.log.msg( - "Deployment spec updated; writing changes to {}".format( - args.deployment_spec - ) + f"Deployment spec updated; writing changes to {args.deployment_spec}" ) with open(args.deployment_spec, "w") as f: json.dump(context.spec, f, indent=2) diff --git a/monitoring/deployment_manager/infrastructure.py b/monitoring/deployment_manager/infrastructure.py index 5f2d43e4cd..db3224a655 100644 --- a/monitoring/deployment_manager/infrastructure.py +++ b/monitoring/deployment_manager/infrastructure.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from dataclasses import dataclass -from typing import Callable, Dict, Optional import kubernetes import structlog @@ -8,17 +8,17 @@ @dataclass -class Clients(object): +class Clients: core: kubernetes.client.CoreV1Api apps: kubernetes.client.AppsV1Api networking: kubernetes.client.NetworkingV1Api @dataclass -class Context(object): +class Context: spec: DeploymentSpec log: structlog.BoundLogger - clients: Optional[Clients] + clients: Clients | None def make_context(spec: DeploymentSpec): @@ -31,15 +31,11 @@ def make_context(spec: DeploymentSpec): ] if not matching_contexts: raise ValueError( - "Cannot find definition for context `{}` in kube-config file".format( - spec.cluster.name - ) + f"Cannot find definition for context `{spec.cluster.name}` in kube-config file" ) if len(matching_contexts) > 1: raise ValueError( - "Found multiple context definitions with the name `{}` in kube-config file".format( - spec.cluster.name - ) + f"Found multiple context definitions with the name `{spec.cluster.name}` in kube-config file" ) api_client = kubernetes.config.new_client_from_config( @@ -58,7 +54,7 @@ def make_context(spec: DeploymentSpec): return Context(spec=spec, log=log, clients=clients) -actions: Dict[str, Callable[[Context], None]] = {} +actions: dict[str, Callable[[Context], None]] = {} def deployment_action(name: str): diff --git a/monitoring/deployment_manager/systems/configuration.py b/monitoring/deployment_manager/systems/configuration.py index 8c343b9797..bd512166d4 100644 --- a/monitoring/deployment_manager/systems/configuration.py +++ b/monitoring/deployment_manager/systems/configuration.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict from monitoring.deployment_manager.systems.dss.configuration import DSS @@ -16,11 +14,11 @@ class KubernetesCluster(ImplicitDict): class DeploymentSpec(ImplicitDict): - cluster: Optional[KubernetesCluster] + cluster: KubernetesCluster | None """Definition of Kubernetes cluster containing this deployment.""" - test: Optional[Test] + test: Test | None """Test systems in this deployment.""" - dss: Optional[DSS] + dss: DSS | None """DSS instance in this deployment.""" diff --git a/monitoring/deployment_manager/systems/test/configuration.py b/monitoring/deployment_manager/systems/test/configuration.py index 3f5f2f9f4e..09b861b134 100644 --- a/monitoring/deployment_manager/systems/test/configuration.py +++ b/monitoring/deployment_manager/systems/test/configuration.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict @@ -8,4 +6,4 @@ class TestV1(ImplicitDict): class Test(ImplicitDict): - v1: Optional[TestV1] + v1: TestV1 | None diff --git a/monitoring/deployment_manager/systems/test/hello_world.py b/monitoring/deployment_manager/systems/test/hello_world.py index 808e5fa37b..60ad9df200 100644 --- a/monitoring/deployment_manager/systems/test/hello_world.py +++ b/monitoring/deployment_manager/systems/test/hello_world.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from kubernetes import client as k8s @@ -24,8 +24,8 @@ def _define_webserver_deployment() -> k8s.V1Deployment: image="hashicorp/http-echo", command=[ "/http-echo", - "-listen=:{}".format(CONTAINER_PORT), - '-text="Echo server on port {}"'.format(CONTAINER_PORT), + f"-listen=:{CONTAINER_PORT}", + f'-text="Echo server on port {CONTAINER_PORT}"', ], ports=[k8s.V1ContainerPort(container_port=CONTAINER_PORT)], resources=k8s.V1ResourceRequirements( @@ -109,7 +109,7 @@ def _define_webserver_ingress() -> k8s.V1Ingress: return ingress -def define_resources() -> List[Any]: +def define_resources() -> list[Any]: return [ _define_webserver_deployment(), _define_webserver_service(), diff --git a/monitoring/get_access_token.py b/monitoring/get_access_token.py index 3acc6237c2..8ad49c7c4a 100644 --- a/monitoring/get_access_token.py +++ b/monitoring/get_access_token.py @@ -1,11 +1,10 @@ import argparse import sys -from typing import List from monitoring.monitorlib import auth -def parse_args(argv: List[str]): +def parse_args(argv: list[str]): parser = argparse.ArgumentParser(description="Retrieve an access token") parser.add_argument( "--spec", diff --git a/monitoring/loadtest/locust_files/ISA.py b/monitoring/loadtest/locust_files/ISA.py index 15fa2f69f3..7b23e00ec2 100644 --- a/monitoring/loadtest/locust_files/ISA.py +++ b/monitoring/loadtest/locust_files/ISA.py @@ -30,7 +30,7 @@ def create_isa(self): isa_uuid = str(uuid.uuid4()) resp = self.client.put( - "/identification_service_areas/{}".format(isa_uuid), + f"/identification_service_areas/{isa_uuid}", json={ "extents": { "spatial_volume": { @@ -59,7 +59,7 @@ def update_isa(self): time_start = datetime.datetime.now(datetime.UTC) time_end = datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=2) resp = self.client.put( - "/identification_service_areas/{}/{}".format(target_isa, target_version), + f"/identification_service_areas/{target_isa}/{target_version}", json={ "extents": { "spatial_volume": { @@ -86,7 +86,7 @@ def get_isa(self): if not target_isa: print("Nothing to pick from isa_dict for GET") return - self.client.get("/identification_service_areas/{}".format(target_isa)) + self.client.get(f"/identification_service_areas/{target_isa}") @task(1) def delete_isa(self): @@ -95,7 +95,7 @@ def delete_isa(self): print("Nothing to pick from isa_dict for DELETE") return self.client.delete( - "/identification_service_areas/{}/{}".format(target_isa, target_version) + f"/identification_service_areas/{target_isa}/{target_version}" ) def checkout_isa(self): diff --git a/monitoring/loadtest/locust_files/Sub.py b/monitoring/loadtest/locust_files/Sub.py index c7a12c9ff0..d2582f7682 100644 --- a/monitoring/loadtest/locust_files/Sub.py +++ b/monitoring/loadtest/locust_files/Sub.py @@ -32,7 +32,7 @@ def create_sub(self): sub_uuid = str(uuid.uuid4()) resp = self.client.put( - "/subscriptions/{}".format(sub_uuid), + f"/subscriptions/{sub_uuid}", json={ "extents": { "spatial_volume": { @@ -59,7 +59,7 @@ def get_sub(self): if not target_sub: print("Nothing to pick from sub_dict for GET") return - self.client.get("/subscriptions/{}".format(target_sub)) + self.client.get(f"/subscriptions/{target_sub}") @task(50) def update_sub(self): @@ -71,7 +71,7 @@ def update_sub(self): time_start = datetime.datetime.now(datetime.UTC) time_end = datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=2) resp = self.client.put( - "/subscriptions/{}/{}".format(target_sub, target_version), + f"/subscriptions/{target_sub}/{target_version}", json={ "extents": { "spatial_volume": { @@ -96,7 +96,7 @@ def delete_sub(self): if not target_sub: print("Nothing to pick from sub_dict for DELETE") return - self.client.delete("/subscriptions/{}/{}".format(target_sub, target_version)) + self.client.delete(f"/subscriptions/{target_sub}/{target_version}") def checkout_sub(self): self.lock.acquire() diff --git a/monitoring/loadtest/locust_files/client.py b/monitoring/loadtest/locust_files/client.py index 480c527e97..e94d6f66ad 100644 --- a/monitoring/loadtest/locust_files/client.py +++ b/monitoring/loadtest/locust_files/client.py @@ -2,7 +2,6 @@ import os import time -import typing from locust import User from uas_standards.astm.f3411.v19.constants import Scope @@ -58,11 +57,11 @@ def log_exception( class USS(User): # Suggested by Locust 1.2.2 API Docs https://docs.locust.io/en/stable/api.html#locust.User.abstract abstract = True - isa_dict: typing.Dict[str, str] = {} - sub_dict: typing.Dict[str, str] = {} + isa_dict: dict[str, str] = {} + sub_dict: dict[str, str] = {} def __init__(self, *args, **kwargs): - super(USS, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) auth_spec = os.environ.get("AUTH_SPEC") oauth_adapter = auth.make_auth_adapter(auth_spec) if auth_spec else None self.client = DSSClient(self.host, oauth_adapter) diff --git a/monitoring/mock_uss/__init__.py b/monitoring/mock_uss/__init__.py index 12f47e585c..6ab5a823a3 100644 --- a/monitoring/mock_uss/__init__.py +++ b/monitoring/mock_uss/__init__.py @@ -1,6 +1,7 @@ import inspect import os -from typing import Any, Callable, Optional +from collections.abc import Callable +from typing import Any # Because mock_uss uses gevent, we need to monkey-patch before anything else is loaded. # https://www.gevent.org/intro.html#monkey-patching @@ -35,8 +36,8 @@ def import_environment_variable( var_name: str, required: bool = True, - default: Optional[str] = None, - mutator: Optional[Callable[[str], Any]] = None, + default: str | None = None, + mutator: Callable[[str], Any] | None = None, ) -> None: """Import a value from a named environment variable into the webapp configuration. @@ -134,7 +135,7 @@ def require_config_value(config_key: str) -> None: msg = ( "################################################################################\n" + "################################ Configuration ################################\n" - + "\n".join("## {}: {}".format(key, webapp.config[key]) for key in webapp.config) + + "\n".join(f"## {key}: {webapp.config[key]}" for key in webapp.config) + "\n" + "################################################################################" ) diff --git a/monitoring/mock_uss/database.py b/monitoring/mock_uss/database.py index 0d4467f965..95d0be5da2 100644 --- a/monitoring/mock_uss/database.py +++ b/monitoring/mock_uss/database.py @@ -1,5 +1,4 @@ import json -from typing import Dict, List, Optional from implicitdict import ImplicitDict, StringBasedDateTime, StringBasedTimeDelta @@ -8,8 +7,8 @@ class PeriodicTaskStatus(ImplicitDict): - last_execution_time: Optional[StringBasedDateTime] = None - period: Optional[StringBasedTimeDelta] = None + last_execution_time: StringBasedDateTime | None = None + period: StringBasedTimeDelta | None = None executing: bool = False @@ -32,19 +31,19 @@ def from_exception(trigger: str, e: BaseException): class Database(ImplicitDict): """Simple in-memory pseudo-database tracking the state of the mock system""" - one_time_tasks: List[str] + one_time_tasks: list[str] """Names of one-time tasks that a process has already initiated""" - task_errors: List[TaskError] + task_errors: list[TaskError] """Information about task errors encountered while running""" stopping: bool = False """True only when the mock_uss should be stopping""" - periodic_tasks: Dict[str, PeriodicTaskStatus] + periodic_tasks: dict[str, PeriodicTaskStatus] """Tasks to perform periodically, by name""" - most_recent_periodic_check: Optional[StringBasedDateTime] + most_recent_periodic_check: StringBasedDateTime | None """Timestamp of most recent time periodic task loop iterated""" diff --git a/monitoring/mock_uss/dynamic_configuration/routes.py b/monitoring/mock_uss/dynamic_configuration/routes.py index 93137a2d2f..80e92e756e 100644 --- a/monitoring/mock_uss/dynamic_configuration/routes.py +++ b/monitoring/mock_uss/dynamic_configuration/routes.py @@ -1,5 +1,3 @@ -from typing import Tuple - import flask from implicitdict import ImplicitDict @@ -14,7 +12,7 @@ @webapp.route("/configuration/locality", methods=["GET"]) -def locality_get() -> Tuple[str, int]: +def locality_get() -> tuple[str, int]: return flask.jsonify( GetLocalityResponse(locality_code=get_locality().locality_code()) ) @@ -22,7 +20,7 @@ def locality_get() -> Tuple[str, int]: @webapp.route("/configuration/locality", methods=["PUT"]) @requires_scope(MOCK_USS_CONFIG_SCOPE) # TODO: use separate public key for this -def locality_set() -> Tuple[str, int]: +def locality_set() -> tuple[str, int]: """Set the locality of the mock_uss.""" try: json = flask.request.json diff --git a/monitoring/mock_uss/f3548v21/flight_planning.py b/monitoring/mock_uss/f3548v21/flight_planning.py index b24d81dc99..9d9241a7f9 100644 --- a/monitoring/mock_uss/f3548v21/flight_planning.py +++ b/monitoring/mock_uss/f3548v21/flight_planning.py @@ -1,6 +1,6 @@ import uuid +from collections.abc import Callable from datetime import datetime -from typing import Callable, Dict, List, Optional, Tuple import arrow import requests @@ -92,10 +92,10 @@ def validate_request(op_intent: f3548_v21.OperationalIntent) -> None: def check_for_disallowed_conflicts( new_op_intent: f3548_v21.OperationalIntent, - existing_flight: Optional[FlightRecord], - op_intents: List[f3548_v21.OperationalIntent], + existing_flight: FlightRecord | None, + op_intents: list[f3548_v21.OperationalIntent], locality: Locality, - log: Optional[Callable[[str], None]] = None, + log: Callable[[str], None] | None = None, ) -> None: """Raise a PlannerError if there are any disallowed conflicts. @@ -172,8 +172,8 @@ def log(msg): def op_intent_transition_valid( - transition_from: Optional[scd_api.OperationalIntentState], - transition_to: Optional[scd_api.OperationalIntentState], + transition_from: scd_api.OperationalIntentState | None, + transition_to: scd_api.OperationalIntentState | None, ) -> bool: valid_states = { scd_api.OperationalIntentState.Accepted, @@ -323,7 +323,7 @@ def op_intent_from_flightinfo( ovn="UNKNOWN", time_start=v4c.time_start.to_f3548v21(), time_end=v4c.time_end.to_f3548v21(), - uss_base_url="{}/mock/scd".format(webapp.config[KEY_BASE_URL]), + uss_base_url=f"{webapp.config[KEY_BASE_URL]}/mock/scd", subscription_id="UNKNOWN", ) if "astm_f3548_21" in flight_info and flight_info.astm_f3548_21: @@ -373,7 +373,7 @@ def op_intent_from_flightrecord( def query_operational_intents( locality: Locality, area_of_interest: f3548_v21.Volume4D, -) -> List[f3548_v21.OperationalIntent]: +) -> list[f3548_v21.OperationalIntent]: """Retrieve a complete set of operational intents in an area, including details. :param locality: Locality applicable to this query @@ -490,10 +490,10 @@ def get_down_uss_op_intent( def check_op_intent( new_flight: FlightRecord, - existing_flight: Optional[FlightRecord], + existing_flight: FlightRecord | None, locality: Locality, log: Callable[[str], None], -) -> List[f3548_v21.EntityOVN]: +) -> list[f3548_v21.EntityOVN]: # Check the transition is valid state_transition_from = ( f3548_v21.OperationalIntentState(existing_flight.op_intent.reference.state) @@ -555,10 +555,10 @@ def check_op_intent( def share_op_intent( new_flight: FlightRecord, - existing_flight: Optional[FlightRecord], - key: List[f3548_v21.EntityOVN], + existing_flight: FlightRecord | None, + key: list[f3548_v21.EntityOVN], log: Callable[[str], None], -) -> Tuple[FlightRecord, Dict[f3548_v21.SubscriptionUssBaseURL, Exception]]: +) -> tuple[FlightRecord, dict[f3548_v21.SubscriptionUssBaseURL, Exception]]: """Share the operational intent reference with the DSS in compliance with ASTM F3548-21. Returns: @@ -619,7 +619,7 @@ def share_op_intent( def delete_op_intent( op_intent_ref: f3548_v21.OperationalIntentReference, log: Callable[[str], None] -) -> Dict[f3548_v21.SubscriptionUssBaseURL, Exception]: +) -> dict[f3548_v21.SubscriptionUssBaseURL, Exception]: """Remove the operational intent reference from the DSS in compliance with ASTM F3548-21. Args: @@ -646,18 +646,18 @@ def delete_op_intent( def notify_subscribers( op_intent_id: f3548_v21.EntityID, - op_intent: Optional[f3548_v21.OperationalIntent], - subscribers: List[f3548_v21.SubscriberToNotify], + op_intent: f3548_v21.OperationalIntent | None, + subscribers: list[f3548_v21.SubscriberToNotify], log: Callable[[str], None], -) -> Dict[f3548_v21.SubscriptionUssBaseURL, Exception]: +) -> dict[f3548_v21.SubscriptionUssBaseURL, Exception]: """ Notify subscribers of a changed or deleted operational intent. This function will attempt all notifications, even if some of them fail. :return: Notification errors if any, by subscriber. """ - notif_errors: Dict[f3548_v21.SubscriptionUssBaseURL, Exception] = {} - base_url = "{}/mock/scd".format(webapp.config[KEY_BASE_URL]) + notif_errors: dict[f3548_v21.SubscriptionUssBaseURL, Exception] = {} + base_url = f"{webapp.config[KEY_BASE_URL]}/mock/scd" for subscriber in subscribers: if subscriber.uss_base_url == base_url: # Do not notify ourselves diff --git a/monitoring/mock_uss/f3548v21/routes_scd.py b/monitoring/mock_uss/f3548v21/routes_scd.py index cd1c251baf..804388da2c 100644 --- a/monitoring/mock_uss/f3548v21/routes_scd.py +++ b/monitoring/mock_uss/f3548v21/routes_scd.py @@ -1,6 +1,5 @@ import json import uuid -from typing import Optional import flask from implicitdict import ImplicitDict @@ -37,9 +36,7 @@ def scdsc_get_operational_intent_details(entityid: str): return ( flask.jsonify( ErrorResponse( - message="Operational intent {} not known by this USS".format( - entityid - ) + message=f"Operational intent {entityid} not known by this USS" ) ), 404, @@ -61,7 +58,7 @@ def scdsc_get_operational_intent_telemetry(entityid: str): # Look up entityid in database tx = db.value - flight: Optional[FlightRecord] = None + flight: FlightRecord | None = None for f in tx.flights.values(): if f and f.op_intent.reference.id == entityid: flight = f @@ -72,9 +69,7 @@ def scdsc_get_operational_intent_telemetry(entityid: str): return ( flask.jsonify( ErrorResponse( - message="Operational intent {} not known by this USS".format( - entityid - ) + message=f"Operational intent {entityid} not known by this USS" ) ), 404, diff --git a/monitoring/mock_uss/flight_planning/routes.py b/monitoring/mock_uss/flight_planning/routes.py index 3aea093e14..4e7d3427ad 100644 --- a/monitoring/mock_uss/flight_planning/routes.py +++ b/monitoring/mock_uss/flight_planning/routes.py @@ -1,7 +1,6 @@ import os import uuid from datetime import timedelta -from typing import Tuple import flask from implicitdict import ImplicitDict @@ -35,12 +34,12 @@ @webapp.route("/flight_planning/v1/status", methods=["GET"]) @requires_scope(Scope.DirectAutomatedTest) -def flight_planning_v1_status() -> Tuple[str, int]: +def flight_planning_v1_status() -> tuple[str, int]: json, code = injection_status() return flask.jsonify(json), code -def injection_status() -> Tuple[dict, int]: +def injection_status() -> tuple[dict, int]: return ( api.StatusResponse( status=api.StatusResponseStatus.Ready, @@ -54,7 +53,7 @@ def injection_status() -> Tuple[dict, int]: @webapp.route("/flight_planning/v1/flight_plans/", methods=["PUT"]) @requires_scope(Scope.Plan) @idempotent_request() -def flight_planning_v1_upsert_flight_plan(flight_plan_id: str) -> Tuple[str, int]: +def flight_planning_v1_upsert_flight_plan(flight_plan_id: str) -> tuple[str, int]: def log(msg: str) -> None: logger.debug(f"[upsert_plan/{os.getpid()}:{flight_plan_id}] {msg}") @@ -67,7 +66,7 @@ def log(msg: str) -> None: json, MockUSSUpsertFlightPlanRequest ) except ValueError as e: - msg = "Create flight {} unable to parse JSON: {}".format(flight_plan_id, e) + msg = f"Create flight {flight_plan_id} unable to parse JSON: {e}" return msg, 400 existing_flight = lock_flight(flight_plan_id, log) @@ -100,7 +99,7 @@ def log(msg: str) -> None: @webapp.route("/flight_planning/v1/flight_plans/", methods=["DELETE"]) @requires_scope(Scope.Plan) -def flight_planning_v1_delete_flight(flight_plan_id: str) -> Tuple[str, int]: +def flight_planning_v1_delete_flight(flight_plan_id: str) -> tuple[str, int]: """Implements flight deletion in SCD automated testing injection API.""" del_resp, status_code = delete_flight(flight_plan_id) @@ -117,14 +116,14 @@ def flight_planning_v1_delete_flight(flight_plan_id: str) -> Tuple[str, int]: @webapp.route("/flight_planning/v1/clear_area_requests", methods=["POST"]) @requires_scope(Scope.DirectAutomatedTest) @idempotent_request() -def flight_planning_v1_clear_area() -> Tuple[str, int]: +def flight_planning_v1_clear_area() -> tuple[str, int]: try: json = flask.request.json if json is None: raise ValueError("Request did not contain a JSON payload") req: api.ClearAreaRequest = ImplicitDict.parse(json, api.ClearAreaRequest) except ValueError as e: - msg = "Unable to parse ClearAreaRequest JSON request: {}".format(e) + msg = f"Unable to parse ClearAreaRequest JSON request: {e}" return msg, 400 clear_resp = clear_area(Volume4D.from_flight_planning_api(req.extent)) diff --git a/monitoring/mock_uss/flights/database.py b/monitoring/mock_uss/flights/database.py index 1744e29055..6ef5a0218a 100644 --- a/monitoring/mock_uss/flights/database.py +++ b/monitoring/mock_uss/flights/database.py @@ -1,6 +1,5 @@ import json from datetime import timedelta -from typing import Dict, Optional from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21.api import OperationalIntent @@ -19,15 +18,15 @@ class FlightRecord(ImplicitDict): flight_info: FlightInfo op_intent: OperationalIntent - mod_op_sharing_behavior: Optional[MockUssFlightBehavior] = None + mod_op_sharing_behavior: MockUssFlightBehavior | None = None locked: bool = False class Database(ImplicitDict): """Simple in-memory pseudo-database tracking the state of the mock system""" - flights: Dict[str, Optional[FlightRecord]] = {} - cached_operations: Dict[str, OperationalIntent] = {} + flights: dict[str, FlightRecord | None] = {} + cached_operations: dict[str, OperationalIntent] = {} db = SynchronizedValue( diff --git a/monitoring/mock_uss/flights/planning.py b/monitoring/mock_uss/flights/planning.py index 34e70dfa28..0213c8062e 100644 --- a/monitoring/mock_uss/flights/planning.py +++ b/monitoring/mock_uss/flights/planning.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from datetime import UTC, datetime -from typing import Callable, Optional from monitoring.mock_uss.flights.database import DEADLOCK_TIMEOUT, FlightRecord, db from monitoring.monitorlib.delay import sleep @@ -47,7 +47,7 @@ def release_flight_lock(flight_id: str, log: Callable[[str], None]) -> None: del tx.flights[flight_id] -def delete_flight_record(flight_id: str) -> Optional[FlightRecord]: +def delete_flight_record(flight_id: str) -> FlightRecord | None: deadline = datetime.now(UTC) + DEADLOCK_TIMEOUT while True: with db as tx: diff --git a/monitoring/mock_uss/geoawareness/check.py b/monitoring/mock_uss/geoawareness/check.py index a7505a73ef..c278d3bd52 100644 --- a/monitoring/mock_uss/geoawareness/check.py +++ b/monitoring/mock_uss/geoawareness/check.py @@ -1,5 +1,4 @@ import logging -from typing import Dict, List from uas_standards.interuss.automated_testing.geo_awareness.v1.api import ( GeozoneHttpsSourceFormat, @@ -27,10 +26,10 @@ def combine_results( return GeozonesCheckResultGeozone.Absent -def check_geozones(req: GeozonesCheckRequest) -> List[GeozonesCheckResultGeozone]: - sources: Dict[str, SourceRecord] = Database.get_sources(db) +def check_geozones(req: GeozonesCheckRequest) -> list[GeozonesCheckResultGeozone]: + sources: dict[str, SourceRecord] = Database.get_sources(db) - results: List[GeozonesCheckResultGeozone] = [ + results: list[GeozonesCheckResultGeozone] = [ GeozonesCheckResultGeozone.Absent ] * len(req.checks) diff --git a/monitoring/mock_uss/geoawareness/database.py b/monitoring/mock_uss/geoawareness/database.py index 1cb9a60819..8c4b233579 100644 --- a/monitoring/mock_uss/geoawareness/database.py +++ b/monitoring/mock_uss/geoawareness/database.py @@ -1,5 +1,4 @@ import json -from typing import Dict, Optional from implicitdict import ImplicitDict from uas_standards.eurocae_ed269 import ED269Schema @@ -18,14 +17,14 @@ class ExistingRecordException(ValueError): class SourceRecord(ImplicitDict): definition: CreateGeozoneSourceRequest state: GeozoneSourceResponseResult - message: Optional[str] - geozone_ed269: Optional[ED269Schema] + message: str | None + geozone_ed269: ED269Schema | None class Database(ImplicitDict): """Simple pseudo-database structure tracking the state of the mock system""" - sources: Dict[str, SourceRecord] = {} + sources: dict[str, SourceRecord] = {} @staticmethod def get_source(db: SynchronizedValue, id: str) -> SourceRecord: @@ -41,7 +40,7 @@ def insert_source( id: str, definition: CreateGeozoneSourceRequest, state: GeozoneSourceResponseResult, - message: Optional[str] = None, + message: str | None = None, ) -> SourceRecord: with db as tx: if id in tx.sources.keys(): @@ -57,7 +56,7 @@ def update_source_state( db: SynchronizedValue, id: str, state: GeozoneSourceResponseResult, - message: Optional[str] = None, + message: str | None = None, ): with db as tx: tx.sources[id]["state"] = state diff --git a/monitoring/mock_uss/geoawareness/ed269.py b/monitoring/mock_uss/geoawareness/ed269.py index f1714a36b2..071bf59cce 100644 --- a/monitoring/mock_uss/geoawareness/ed269.py +++ b/monitoring/mock_uss/geoawareness/ed269.py @@ -2,7 +2,6 @@ import json import logging from datetime import datetime -from typing import List, Optional, Union import s2sphere from implicitdict import StringBasedDateTime @@ -48,7 +47,7 @@ def convert_distance( ) -def evaluate_position(feature: UASZoneVersion, position: Optional[Position]) -> bool: +def evaluate_position(feature: UASZoneVersion, position: Position | None) -> bool: logger.debug(f" _evaluate_position: position:{position} feature:{feature}") if position is None: logger.debug(" * position is None => True") @@ -100,8 +99,8 @@ def evaluate_position(feature: UASZoneVersion, position: Optional[Position]) -> def _is_in_date_range( start: StringBasedDateTime, end: StringBasedDateTime, - after: Optional[StringBasedDateTime], - before: Optional[StringBasedDateTime], + after: StringBasedDateTime | None, + before: StringBasedDateTime | None, ) -> bool: if after is None and before is None: return True @@ -121,8 +120,8 @@ def _is_in_date_range( def evaluate_timing( feature: UASZoneVersion, - after: Optional[StringBasedDateTime] = None, - before: Optional[StringBasedDateTime] = None, + after: StringBasedDateTime | None = None, + before: StringBasedDateTime | None = None, ) -> bool: logger.debug( f" _evaluate_timing: after:{after} before:{before} feature:{feature}" @@ -153,7 +152,7 @@ def evaluate_timing( return False -def _adjust_uspace_class(uspace_class: Union[str, List[str]]) -> List[str]: +def _adjust_uspace_class(uspace_class: str | list[str]) -> list[str]: # TODO: Revisit when new version of ED-269 will be published. # uSpaceClass field is currently defined in ED269 standard as a string. # The current assumption is that uSpaceClass will be a string or some sort of an array of values. @@ -180,9 +179,7 @@ def _adjust_uspace_class(uspace_class: Union[str, List[str]]) -> List[str]: return uspace_class -def evaluate_non_spacetime( - feature: UASZoneVersion, ed269: Optional[ED269Filters] -) -> bool: +def evaluate_non_spacetime(feature: UASZoneVersion, ed269: ED269Filters | None) -> bool: """Returns True if the feature matches all provided filters""" logger.debug(f" _evaluate_ed269: ed269:{ed269} feature:{feature}") @@ -190,7 +187,7 @@ def evaluate_non_spacetime( return True uspace_class_filter = ed269.get("uSpaceClass", None) - uspace_class: Optional[List[str]] = _adjust_uspace_class( + uspace_class: list[str] | None = _adjust_uspace_class( feature.get("uSpaceClass", None) ) @@ -238,7 +235,7 @@ def evaluate_feature(feature: UASZoneVersion, filter_set: GeozonesFilterSet) -> def evaluate_features( - features: List[UASZoneVersion], filter_set: GeozonesFilterSet + features: list[UASZoneVersion], filter_set: GeozonesFilterSet ) -> GeozonesCheckResultGeozone: logger.debug(f" Evalutating {len(features)} features:") @@ -250,7 +247,7 @@ def evaluate_features( return GeozonesCheckResultGeozone.Absent -def evaluate_source(source: SourceRecord, filter_sets: List[GeozonesFilterSet]): +def evaluate_source(source: SourceRecord, filter_sets: list[GeozonesFilterSet]): if not ( source.state == GeozoneSourceResponseResult.Ready and "geozone_ed269" in source ): diff --git a/monitoring/mock_uss/geoawareness/routes_geoawareness.py b/monitoring/mock_uss/geoawareness/routes_geoawareness.py index 33b69a0877..8bdb60f9de 100644 --- a/monitoring/mock_uss/geoawareness/routes_geoawareness.py +++ b/monitoring/mock_uss/geoawareness/routes_geoawareness.py @@ -1,5 +1,3 @@ -from typing import Tuple - import flask from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.geo_awareness.v1.api import ( @@ -26,7 +24,7 @@ methods=["GET"], ) @requires_scope(SCOPE_GEOAWARENESS_TEST) -def geoawareness_get_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: +def geoawareness_get_geozone_sources(geozone_source_id: str) -> tuple[str, int]: return get_geozone_source(geozone_source_id) @@ -35,7 +33,7 @@ def geoawareness_get_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: methods=["PUT"], ) @requires_scope(SCOPE_GEOAWARENESS_TEST) -def geoawareness_put_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: +def geoawareness_put_geozone_sources(geozone_source_id: str) -> tuple[str, int]: try: json = flask.request.json if json is None: @@ -44,9 +42,7 @@ def geoawareness_put_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: json, CreateGeozoneSourceRequest ) except ValueError as e: - msg = "Create geozone source {} unable to parse JSON: {}".format( - geozone_source_id, e - ) + msg = f"Create geozone source {geozone_source_id} unable to parse JSON: {e}" return msg, 400 return create_geozone_source(geozone_source_id, body) @@ -57,7 +53,7 @@ def geoawareness_put_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: methods=["DELETE"], ) @requires_scope(SCOPE_GEOAWARENESS_TEST) -def geoawareness_delete_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: +def geoawareness_delete_geozone_sources(geozone_source_id: str) -> tuple[str, int]: return delete_geozone_source(geozone_source_id) @@ -70,7 +66,7 @@ def geoawareness_check(): raise ValueError("Request did not contain a JSON payload") body: GeozonesCheckRequest = ImplicitDict.parse(json, GeozonesCheckRequest) except ValueError as e: - msg = "Geozone check unable to parse JSON: {}".format(e) + msg = f"Geozone check unable to parse JSON: {e}" return msg, 400 applicable_geozone = check_geozones(body) diff --git a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py index fe63ff6ae4..5fc6a86d4d 100644 --- a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py +++ b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py @@ -1,7 +1,6 @@ import json import os from datetime import datetime -from typing import List, Tuple from flask import Response, jsonify, request from implicitdict import ImplicitDict, StringBasedDateTime @@ -24,7 +23,7 @@ @webapp.route("/mock_uss/interuss_logging/logs", methods=["GET"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) -def interaction_logs() -> Tuple[Response, int]: +def interaction_logs() -> tuple[Response, int]: """ Returns all the interaction logs with requests that were received or initiated between 'from_time' and now @@ -39,7 +38,7 @@ def interaction_logs() -> Tuple[Response, int]: # individual interactions are logged to a file of the form ___.json, eg # 000001_Incoming_GET_2023-08-30T20-48-21.900000Z.json - interactions: List[Interaction] = [] + interactions: list[Interaction] = [] for fname in os.listdir(log_path): # Parse the interaction time from the file name: @@ -57,7 +56,7 @@ def interaction_logs() -> Tuple[Response, int]: ) if interaction_time.datetime < from_time.datetime: continue - with open(os.path.join(log_path, fname), "r") as f: + with open(os.path.join(log_path, fname)) as f: try: obj = json.load(f) interaction = ImplicitDict.parse(obj, Interaction) @@ -74,7 +73,7 @@ def interaction_logs() -> Tuple[Response, int]: @webapp.route("/mock_uss/interuss_logging/logs", methods=["DELETE"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) -def delete_interaction_logs() -> Tuple[str, int]: +def delete_interaction_logs() -> tuple[str, int]: """Deletes all the files under the logging directory""" log_path = webapp.config[KEY_INTERACTIONS_LOG_DIR] diff --git a/monitoring/mock_uss/logging.py b/monitoring/mock_uss/logging.py index 89e0a3af6b..e262cfbf2a 100644 --- a/monitoring/mock_uss/logging.py +++ b/monitoring/mock_uss/logging.py @@ -2,7 +2,6 @@ import json import os -from typing import List, Optional import flask import loguru @@ -19,11 +18,11 @@ def _get_request_id(req: Request) -> str: return "" -class RequestLogger(object): +class RequestLogger: _request_id: str - _loguru_hook: Optional[int] = None + _loguru_hook: int | None = None _report_log: bool = True - messages: List[str] + messages: list[str] def __init__(self): self.messages = [] @@ -63,7 +62,7 @@ def remove_from_loguru(self) -> None: _REQUEST_LOGGER_FIELD = "request_logger" -def get_request_logger() -> Optional[RequestLogger]: +def get_request_logger() -> RequestLogger | None: if hasattr(flask.request, _REQUEST_LOGGER_FIELD): return getattr(flask.request, _REQUEST_LOGGER_FIELD) else: @@ -93,7 +92,7 @@ def add_request_log(resp: Response): @webapp.teardown_request -def end_request_log(e: Optional[BaseException]) -> None: +def end_request_log(e: BaseException | None) -> None: request_logger = get_request_logger() if request_logger is not None: request_logger.remove_from_loguru() diff --git a/monitoring/mock_uss/msgsigning/routes_msgsigning.py b/monitoring/mock_uss/msgsigning/routes_msgsigning.py index f90fce4325..8abc42e5e0 100644 --- a/monitoring/mock_uss/msgsigning/routes_msgsigning.py +++ b/monitoring/mock_uss/msgsigning/routes_msgsigning.py @@ -16,7 +16,7 @@ def get_public_key(): webapp.config.get(config.KEY_CERT_BASE_PATH), db.value.public_key_name ) - logger.info("Retrieving public key file from {}".format(public_key_file_location)) + logger.info(f"Retrieving public key file from {public_key_file_location}") return flask.send_file(public_key_file_location) diff --git a/monitoring/mock_uss/riddp/behavior.py b/monitoring/mock_uss/riddp/behavior.py index 30233f4fd2..ab5821361a 100644 --- a/monitoring/mock_uss/riddp/behavior.py +++ b/monitoring/mock_uss/riddp/behavior.py @@ -1,10 +1,8 @@ -from typing import List, Optional - from implicitdict import ImplicitDict ServiceProviderID = str class DisplayProviderBehavior(ImplicitDict): - always_omit_recent_paths: Optional[bool] = False - do_not_display_flights_from: Optional[List[ServiceProviderID]] = [] + always_omit_recent_paths: bool | None = False + do_not_display_flights_from: list[ServiceProviderID] | None = [] diff --git a/monitoring/mock_uss/riddp/clustering.py b/monitoring/mock_uss/riddp/clustering.py index 2293f99050..50492216f2 100644 --- a/monitoring/mock_uss/riddp/clustering.py +++ b/monitoring/mock_uss/riddp/clustering.py @@ -1,6 +1,5 @@ import math import random -from typing import List import s2sphere from implicitdict import ImplicitDict @@ -13,7 +12,7 @@ from monitoring.monitorlib.rid import RIDVersion -class Point(object): +class Point: x: float y: float @@ -27,7 +26,7 @@ class Cluster(ImplicitDict): x_max: float y_min: float y_max: float - points: List[Point] + points: list[Point] def width(self): return math.fabs(self.x_max - self.x_min) @@ -97,16 +96,16 @@ def extend(self, rid_version: RIDVersion, view_area_sqm: float): def make_clusters( - flights: List[observation_api.Flight], + flights: list[observation_api.Flight], view_min: s2sphere.LatLng, view_max: s2sphere.LatLng, rid_version: RIDVersion, -) -> List[observation_api.Cluster]: +) -> list[observation_api.Cluster]: if not flights: return [] # Make the initial cluster - points: List[Point] = [ + points: list[Point] = [ Point( *geo.flatten( view_min, @@ -118,7 +117,7 @@ def make_clusters( for flight in flights ] x_max, y_max = geo.flatten(view_min, view_max) - clusters: List[Cluster] = [ + clusters: list[Cluster] = [ Cluster(x_min=0, y_min=0, x_max=x_max, y_max=y_max, points=points) ] @@ -126,7 +125,7 @@ def make_clusters( view_area_sqm = geo.area_of_latlngrect(LatLngRect(view_min, view_max)) - result: List[observation_api.Cluster] = [] + result: list[observation_api.Cluster] = [] for cluster in clusters: cluster = cluster.extend(rid_version, view_area_sqm) diff --git a/monitoring/mock_uss/riddp/database.py b/monitoring/mock_uss/riddp/database.py index 983e5b96b0..0ab4adcc2f 100644 --- a/monitoring/mock_uss/riddp/database.py +++ b/monitoring/mock_uss/riddp/database.py @@ -1,5 +1,4 @@ import json -from typing import Dict, List from implicitdict import ImplicitDict @@ -20,9 +19,9 @@ class ObservationSubscription(ImplicitDict): upsert_result: ChangedSubscription - updates: List[UpdatedISA] + updates: list[UpdatedISA] - def get_isas(self) -> List[ISA]: + def get_isas(self) -> list[ISA]: isas = [isa for isa in self.upsert_result.isas] # TODO: consider sorting updates by notification index for update in self.updates: @@ -43,7 +42,7 @@ def get_isas(self) -> List[ISA]: return isas @property - def flights_urls(self) -> Dict[str, str]: + def flights_urls(self) -> dict[str, str]: """Returns map of flights URL to owning USS""" return {isa.flights_url: isa.owner for isa in self.get_isas()} @@ -51,9 +50,9 @@ def flights_urls(self) -> Dict[str, str]: class Database(ImplicitDict): """Simple pseudo-database structure tracking the state of the mock system""" - flights: Dict[str, FlightInfo] + flights: dict[str, FlightInfo] behavior: DisplayProviderBehavior = DisplayProviderBehavior() - subscriptions: List[ObservationSubscription] + subscriptions: list[ObservationSubscription] db = SynchronizedValue( diff --git a/monitoring/mock_uss/riddp/routes_behavior.py b/monitoring/mock_uss/riddp/routes_behavior.py index fb618a880e..df363f055c 100644 --- a/monitoring/mock_uss/riddp/routes_behavior.py +++ b/monitoring/mock_uss/riddp/routes_behavior.py @@ -1,5 +1,3 @@ -from typing import Tuple - import flask from implicitdict import ImplicitDict @@ -10,7 +8,7 @@ @webapp.route("/riddp/behavior", methods=["PUT"]) -def riddp_set_dp_behavior() -> Tuple[str, int]: +def riddp_set_dp_behavior() -> tuple[str, int]: """Set the behavior of the mock Display Provider.""" try: json = flask.request.json @@ -18,7 +16,7 @@ def riddp_set_dp_behavior() -> Tuple[str, int]: raise ValueError("Request did not contain a JSON payload") dp_behavior = ImplicitDict.parse(json, DisplayProviderBehavior) except ValueError as e: - msg = "Change behavior for Display Provider unable to parse JSON: {}".format(e) + msg = f"Change behavior for Display Provider unable to parse JSON: {e}" return msg, 400 with db as tx: @@ -28,6 +26,6 @@ def riddp_set_dp_behavior() -> Tuple[str, int]: @webapp.route("/riddp/behavior", methods=["GET"]) -def riddp_get_dp_behavior() -> Tuple[str, int]: +def riddp_get_dp_behavior() -> tuple[str, int]: """Get the behavior of the mock Display Provider.""" return flask.jsonify(db.value.behavior) diff --git a/monitoring/mock_uss/riddp/routes_observation.py b/monitoring/mock_uss/riddp/routes_observation.py index 0c62c7e5fe..5511b6046d 100644 --- a/monitoring/mock_uss/riddp/routes_observation.py +++ b/monitoring/mock_uss/riddp/routes_observation.py @@ -1,6 +1,5 @@ import uuid from datetime import timedelta -from typing import Dict, List, Optional, Tuple import arrow import flask @@ -43,9 +42,9 @@ def _make_flight_observation( flight: Flight, view: s2sphere.LatLngRect ) -> observation_api.Flight: - paths: List[List[observation_api.Position]] = [] - current_path: List[observation_api.Position] = [] - previous_position: Optional[observation_api.Position] = None + paths: list[list[observation_api.Position]] = [] + current_path: list[observation_api.Position] = [] + previous_position: observation_api.Position | None = None lat_min = view.lat_lo().degrees lat_max = view.lat_hi().degrees @@ -110,7 +109,7 @@ def _make_flight_observation( @webapp.route("/riddp/observation/display_data", methods=["GET"]) @requires_scope(Scope.Read) -def riddp_display_data() -> Tuple[flask.Response, int]: +def riddp_display_data() -> tuple[flask.Response, int]: """Implements retrieval of current display data per automated testing API.""" if "view" not in flask.request.args: @@ -122,7 +121,7 @@ def riddp_display_data() -> Tuple[flask.Response, int]: view = geo.make_latlng_rect(flask.request.args["view"]) except ValueError as e: return ( - flask.jsonify(ErrorResponse(message="Error parsing view: {}".format(e))), + flask.jsonify(ErrorResponse(message=f"Error parsing view: {e}")), 400, ) @@ -138,7 +137,7 @@ def riddp_display_data() -> Tuple[flask.Response, int]: with db as tx: # Find an existing subscription to serve this request - subscription: Optional[ObservationSubscription] = None + subscription: ObservationSubscription | None = None t_max = ( arrow.utcnow() + timedelta(seconds=1) ).datetime # Don't rely on subscriptions very near their expiration @@ -188,9 +187,9 @@ def riddp_display_data() -> Tuple[flask.Response, int]: tx.subscriptions.append(subscription) # Fetch flights from each unique flights URL - validated_flights: List[Flight] = [] + validated_flights: list[Flight] = [] tx = db.value - flight_info: Dict[str, database.FlightInfo] = {k: v for k, v in tx.flights.items()} + flight_info: dict[str, database.FlightInfo] = {k: v for k, v in tx.flights.items()} behavior: DisplayProviderBehavior = tx.behavior for flights_url, uss in subscription.flights_urls.items(): @@ -244,7 +243,7 @@ def riddp_display_data() -> Tuple[flask.Response, int]: @webapp.route("/riddp/observation/display_data/", methods=["GET"]) @requires_scope(Scope.Read) -def riddp_flight_details(flight_id: str) -> Tuple[str, int]: +def riddp_flight_details(flight_id: str) -> tuple[str, int]: """Implements get flight details endpoint per automated testing API.""" tx = db.value flight_info = tx.flights.get(flight_id) diff --git a/monitoring/mock_uss/riddp/routes_riddp_v19.py b/monitoring/mock_uss/riddp/routes_riddp_v19.py index 7a91e2873a..4586d549a1 100644 --- a/monitoring/mock_uss/riddp/routes_riddp_v19.py +++ b/monitoring/mock_uss/riddp/routes_riddp_v19.py @@ -32,9 +32,7 @@ def riddp_notify_isa_v19(id: str): ImplicitDict.parse(json, PutIdentificationServiceAreaNotificationParameters) ) except ValueError as e: - msg = "Unable to parse PutIdentificationServiceAreaNotificationParameters JSON request: {}".format( - e - ) + msg = f"Unable to parse PutIdentificationServiceAreaNotificationParameters JSON request: {e}" return msg, 400 subscription_ids = [s.subscription_id for s in put_params.subscriptions] diff --git a/monitoring/mock_uss/riddp/routes_riddp_v22a.py b/monitoring/mock_uss/riddp/routes_riddp_v22a.py index 13d638070a..0f6a47c938 100644 --- a/monitoring/mock_uss/riddp/routes_riddp_v22a.py +++ b/monitoring/mock_uss/riddp/routes_riddp_v22a.py @@ -32,9 +32,7 @@ def riddp_notify_isa_v22a(id: str): ImplicitDict.parse(json, PutIdentificationServiceAreaNotificationParameters) ) except ValueError as e: - msg = "Unable to parse PutIdentificationServiceAreaNotificationParameters JSON request: {}".format( - e - ) + msg = f"Unable to parse PutIdentificationServiceAreaNotificationParameters JSON request: {e}" return msg, 400 subscription_ids = [s.subscription_id for s in put_params.subscriptions] diff --git a/monitoring/mock_uss/ridsp/behavior.py b/monitoring/mock_uss/ridsp/behavior.py index 18f1e2174c..0a16f93f3c 100644 --- a/monitoring/mock_uss/ridsp/behavior.py +++ b/monitoring/mock_uss/ridsp/behavior.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict from uas_standards.astm.f3411.v19.api import RIDFlight @@ -10,10 +8,10 @@ class ServiceProviderBehavior(ImplicitDict): - switch_latitude_and_longitude_when_reporting: Optional[bool] = False - use_agl_instead_of_wgs84_for_altitude: Optional[bool] = False - use_feet_instead_of_meters_for_altitude: Optional[bool] = False - delay_flight_report_s: Optional[int] = 0 + switch_latitude_and_longitude_when_reporting: bool | None = False + use_agl_instead_of_wgs84_for_altitude: bool | None = False + use_feet_instead_of_meters_for_altitude: bool | None = False + delay_flight_report_s: int | None = 0 def adjust_reported_flight( diff --git a/monitoring/mock_uss/ridsp/database.py b/monitoring/mock_uss/ridsp/database.py index a096330541..736b47c54c 100644 --- a/monitoring/mock_uss/ridsp/database.py +++ b/monitoring/mock_uss/ridsp/database.py @@ -1,5 +1,4 @@ import json -from typing import Dict, List, Optional from implicitdict import ImplicitDict @@ -14,8 +13,8 @@ class TestRecord(ImplicitDict): """Representation of RID SP's record of a set of injected test flights""" version: str - flights: List[injection_api.TestFlight] - isa_version: Optional[str] = None + flights: list[injection_api.TestFlight] + isa_version: str | None = None def __init__(self, **kwargs): kwargs["flights"] = [ @@ -24,13 +23,13 @@ def __init__(self, **kwargs): for flight in kwargs["flights"]: flight.order_telemetry() - super(TestRecord, self).__init__(**kwargs) + super().__init__(**kwargs) class Database(ImplicitDict): """Simple pseudo-database structure tracking the state of the mock system""" - tests: Dict[str, TestRecord] = {} + tests: dict[str, TestRecord] = {} behavior: ServiceProviderBehavior = ServiceProviderBehavior() notifications: ServiceProviderUserNotifications = ServiceProviderUserNotifications() diff --git a/monitoring/mock_uss/ridsp/routes_behavior.py b/monitoring/mock_uss/ridsp/routes_behavior.py index 110bff1919..5a45f65bba 100644 --- a/monitoring/mock_uss/ridsp/routes_behavior.py +++ b/monitoring/mock_uss/ridsp/routes_behavior.py @@ -1,5 +1,3 @@ -from typing import Tuple - import flask from implicitdict import ImplicitDict @@ -10,7 +8,7 @@ @webapp.route("/ridsp/behavior", methods=["PUT"]) -def ridsp_set_dp_behavior() -> Tuple[str, int]: +def ridsp_set_dp_behavior() -> tuple[str, int]: """Set the behavior of the mock Display Provider.""" try: json = flask.request.json @@ -18,7 +16,7 @@ def ridsp_set_dp_behavior() -> Tuple[str, int]: raise ValueError("Request did not contain a JSON payload") dp_behavior = ImplicitDict.parse(json, ServiceProviderBehavior) except ValueError as e: - msg = "Change behavior for Service Provider unable to parse JSON: {}".format(e) + msg = f"Change behavior for Service Provider unable to parse JSON: {e}" return msg, 400 with db as tx: @@ -28,6 +26,6 @@ def ridsp_set_dp_behavior() -> Tuple[str, int]: @webapp.route("/ridsp/behavior", methods=["GET"]) -def ridsp_get_dp_behavior() -> Tuple[str, int]: +def ridsp_get_dp_behavior() -> tuple[str, int]: """Get the behavior of the mock Display Provider.""" return flask.jsonify(db.value.behavior) diff --git a/monitoring/mock_uss/ridsp/routes_injection.py b/monitoring/mock_uss/ridsp/routes_injection.py index 8f4ade1ab8..4ee4cd3ae8 100644 --- a/monitoring/mock_uss/ridsp/routes_injection.py +++ b/monitoring/mock_uss/ridsp/routes_injection.py @@ -1,6 +1,5 @@ import datetime import uuid -from typing import Tuple import arrow import flask @@ -42,7 +41,7 @@ class ErrorResponse(ImplicitDict): @webapp.route("/ridsp/injection/tests/", methods=["PUT"]) @requires_scope(injection_api.SCOPE_RID_QUALIFIER_INJECT) @idempotent_request() -def ridsp_create_test(test_id: str) -> Tuple[str, int]: +def ridsp_create_test(test_id: str) -> tuple[str, int]: """Implements test creation in RID automated testing injection API.""" logger.info(f"Create test {test_id}") rid_version = webapp.config[KEY_RID_VERSION] @@ -57,7 +56,7 @@ def ridsp_create_test(test_id: str) -> Tuple[str, int]: version=str(uuid.uuid4()), flights=req_body.requested_flights ) except ValueError as e: - msg = "Create test {} unable to parse JSON: {}".format(test_id, e) + msg = f"Create test {test_id} unable to parse JSON: {e}" return msg, 400 # Create ISA in DSS @@ -121,14 +120,14 @@ def ridsp_create_test(test_id: str) -> Tuple[str, int]: @webapp.route("/ridsp/injection/tests//", methods=["DELETE"]) @requires_scope(injection_api.SCOPE_RID_QUALIFIER_INJECT) -def ridsp_delete_test(test_id: str, version: str) -> Tuple[str, int]: +def ridsp_delete_test(test_id: str, version: str) -> tuple[str, int]: """Implements test deletion in RID automated testing injection API.""" logger.info(f"Delete test {test_id}") rid_version = webapp.config[KEY_RID_VERSION] record = db.value.tests.get(test_id, None) if record is None: - return 'Test "{}" not found'.format(test_id), 404 + return f'Test "{test_id}" not found', 404 if record.version != version: return ( @@ -173,7 +172,7 @@ def ridsp_delete_test(test_id: str, version: str) -> Tuple[str, int]: methods=["GET"], ) @requires_scope(injection_api.SCOPE_RID_QUALIFIER_INJECT) -def ridsp_get_user_notifications() -> Tuple[str, int]: +def ridsp_get_user_notifications() -> tuple[str, int]: """Returns the list of user notifications observed by the virtual user""" if "after" not in flask.request.args: diff --git a/monitoring/mock_uss/ridsp/routes_ridsp_v19.py b/monitoring/mock_uss/ridsp/routes_ridsp_v19.py index fc6a3354e9..ddf2b51502 100644 --- a/monitoring/mock_uss/ridsp/routes_ridsp_v19.py +++ b/monitoring/mock_uss/ridsp/routes_ridsp_v19.py @@ -1,6 +1,5 @@ import datetime from datetime import timedelta -from typing import List, Optional import arrow import flask @@ -55,7 +54,7 @@ def _get_report( t_request: datetime.datetime, view: s2sphere.LatLngRect, include_recent_positions: bool, -) -> Optional[RIDFlight]: +) -> RIDFlight | None: details = flight.get_details(t_request) if not details: return None @@ -77,7 +76,7 @@ def _get_report( simulated=True, ) if include_recent_positions: - recent_positions: List[RIDRecentAircraftPosition] = [] + recent_positions: list[RIDRecentAircraftPosition] = [] for recent_state in recent_states: recent_positions.append( RIDRecentAircraftPosition( @@ -118,7 +117,7 @@ def ridsp_flights_v19(): view = geo.make_latlng_rect(flask.request.args["view"]) except ValueError as e: return ( - flask.jsonify(ErrorResponse(message="Error parsing view: {}".format(e))), + flask.jsonify(ErrorResponse(message=f"Error parsing view: {e}")), 400, ) @@ -128,9 +127,7 @@ def ridsp_flights_v19(): diagonal = geo.get_latlngrect_diagonal_km(view) if diagonal > NetMaxDisplayAreaDiagonalKm: - msg = "Requested diagonal of {} km exceeds limit of {} km".format( - diagonal, NetMaxDisplayAreaDiagonalKm - ) + msg = f"Requested diagonal of {diagonal} km exceeds limit of {NetMaxDisplayAreaDiagonalKm} km" return flask.jsonify(ErrorResponse(message=msg)), 413 now = arrow.utcnow().datetime @@ -168,6 +165,6 @@ def ridsp_flight_details_v19(id: str): 200, ) return ( - flask.jsonify(ErrorResponse(message="Flight {} not found".format(id))), + flask.jsonify(ErrorResponse(message=f"Flight {id} not found")), 404, ) diff --git a/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py b/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py index 209d4d3a3c..e5ee3811f8 100644 --- a/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py +++ b/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py @@ -1,6 +1,5 @@ import datetime from datetime import timedelta -from typing import List, Optional import arrow import flask @@ -58,7 +57,7 @@ def _make_state(s: injection.RIDAircraftState) -> RIDAircraftState: def _make_operator_location( - position: injection.LatLngPoint, altitude: Optional[injection.OperatorAltitude] + position: injection.LatLngPoint, altitude: injection.OperatorAltitude | None ) -> OperatorLocation: """Convert injection information to F3411-22a OperatorLocation""" operator_location = OperatorLocation( @@ -108,7 +107,7 @@ def _get_report( t_request: datetime.datetime, view: s2sphere.LatLngRect, recent_positions_duration: float, -) -> Optional[RIDFlight]: +) -> RIDFlight | None: details = flight.get_details(t_request) if not details: return None @@ -130,7 +129,7 @@ def _get_report( simulated=True, ) if recent_positions_duration > 0: - recent_positions: List[RIDRecentAircraftPosition] = [] + recent_positions: list[RIDRecentAircraftPosition] = [] now = arrow.utcnow().datetime for recent_state in recent_states: if ( @@ -175,7 +174,7 @@ def ridsp_flights_v22a(): view = geo.make_latlng_rect(flask.request.args["view"]) except ValueError as e: return ( - flask.jsonify(ErrorResponse(message="Error parsing view: {}".format(e))), + flask.jsonify(ErrorResponse(message=f"Error parsing view: {e}")), 400, ) @@ -204,9 +203,7 @@ def ridsp_flights_v22a(): diagonal = geo.get_latlngrect_diagonal_km(view) if diagonal > NetMaxDisplayAreaDiagonalKm: - msg = "Requested diagonal of {} km exceeds limit of {} km".format( - diagonal, NetMaxDisplayAreaDiagonalKm - ) + msg = f"Requested diagonal of {diagonal} km exceeds limit of {NetMaxDisplayAreaDiagonalKm} km" return flask.jsonify(ErrorResponse(message=msg)), 413 now = arrow.utcnow().datetime @@ -243,6 +240,6 @@ def ridsp_flight_details_v22a(id: str): 200, ) return ( - flask.jsonify(ErrorResponse(message="Flight {} not found".format(id))), + flask.jsonify(ErrorResponse(message=f"Flight {id} not found")), 404, ) diff --git a/monitoring/mock_uss/ridsp/user_notifications.py b/monitoring/mock_uss/ridsp/user_notifications.py index dee4c57f00..0bd613e512 100644 --- a/monitoring/mock_uss/ridsp/user_notifications.py +++ b/monitoring/mock_uss/ridsp/user_notifications.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional, Tuple import arrow from implicitdict import ImplicitDict, StringBasedDateTime @@ -23,8 +22,8 @@ class ServiceProviderUserNotifications(ImplicitDict): def record_notification( self, - message: Optional[str] = None, - observed_at: Optional[datetime.datetime] = None, + message: str | None = None, + observed_at: datetime.datetime | None = None, ): if not observed_at: observed_at = arrow.utcnow().datetime @@ -48,8 +47,8 @@ def create_notifications_if_needed(self, record: "database.TestRecord"): def check_and_generate_missing_fields_notifications( - injected_flights: List[TestFlight], -) -> List[Tuple[datetime.datetime, str]]: + injected_flights: list[TestFlight], +) -> list[tuple[datetime.datetime, str]]: missing_fields_notifications = [] for flight in injected_flights: @@ -94,13 +93,13 @@ def check_and_generate_missing_fields_notifications( def check_and_generate_slow_update_notification( - injected_flights: List[injection_api.TestFlight], -) -> List[datetime]: + injected_flights: list[injection_api.TestFlight], +) -> list[datetime]: """ Iterate over the provided list of injected TestFlight objects and, for any flight that has an average update rate under 1Hz, return a time for which a notification should be sent to the operator. """ - operator_slow_update_notifications: List[datetime] = [] + operator_slow_update_notifications: list[datetime] = [] for f in injected_flights: # Mean rate is not technically correct as per Net0040 # (20% of the samples may be above 1Hz with a mean rate below 1Hz), diff --git a/monitoring/mock_uss/routes.py b/monitoring/mock_uss/routes.py index 98cb902890..22708e8fd5 100644 --- a/monitoring/mock_uss/routes.py +++ b/monitoring/mock_uss/routes.py @@ -31,8 +31,9 @@ def handle_exception(e): return ( flask.jsonify( { - "message": "Invalid scope; expected one of {%s}, but received only {%s}" - % (" ".join(e.permitted_scopes), " ".join(e.provided_scopes)) + "message": "Invalid scope; expected one of {{{}}}, but received only {{{}}}".format( + " ".join(e.permitted_scopes), " ".join(e.provided_scopes) + ) } ), 403, diff --git a/monitoring/mock_uss/scd_injection/routes_injection.py b/monitoring/mock_uss/scd_injection/routes_injection.py index cdd0b79cea..4c9843f52b 100644 --- a/monitoring/mock_uss/scd_injection/routes_injection.py +++ b/monitoring/mock_uss/scd_injection/routes_injection.py @@ -1,6 +1,5 @@ import os from datetime import UTC, datetime, timedelta -from typing import Dict, List, Optional, Tuple import flask import requests.exceptions @@ -68,13 +67,13 @@ @webapp.route("/scdsc/v1/status", methods=["GET"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) -def scdsc_injection_status() -> Tuple[str, int]: +def scdsc_injection_status() -> tuple[str, int]: """Implements USS status in SCD automated testing injection API.""" json, code = injection_status() return flask.jsonify(json), code -def injection_status() -> Tuple[dict, int]: +def injection_status() -> tuple[dict, int]: return ( {"status": "Ready", "version": versioning.get_code_version()}, 200, @@ -83,13 +82,13 @@ def injection_status() -> Tuple[dict, int]: @webapp.route("/scdsc/v1/capabilities", methods=["GET"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) -def scdsc_scd_capabilities() -> Tuple[str, int]: +def scdsc_scd_capabilities() -> tuple[str, int]: """Implements USS capabilities in SCD automated testing injection API.""" json, code = scd_capabilities() return flask.jsonify(json), code -def scd_capabilities() -> Tuple[dict, int]: +def scd_capabilities() -> tuple[dict, int]: return ( CapabilitiesResponse( capabilities=[ @@ -105,7 +104,7 @@ def scd_capabilities() -> Tuple[dict, int]: @webapp.route("/scdsc/v1/flights/", methods=["PUT"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) @idempotent_request() -def scdsc_inject_flight(flight_id: str) -> Tuple[str, int]: +def scdsc_inject_flight(flight_id: str) -> tuple[str, int]: """Implements flight injection in SCD automated testing injection API.""" def log(msg): @@ -118,7 +117,7 @@ def log(msg): raise ValueError("Request did not contain a JSON payload") req_body = ImplicitDict.parse(json, MockUSSInjectFlightRequest) except ValueError as e: - msg = "Create flight {} unable to parse JSON: {}".format(flight_id, e) + msg = f"Create flight {flight_id} unable to parse JSON: {e}" return msg, 400 existing_flight = lock_flight(flight_id, log) @@ -141,7 +140,7 @@ def log(msg): def inject_flight( flight_id: str, new_flight: FlightRecord, - existing_flight: Optional[FlightRecord], + existing_flight: FlightRecord | None, ) -> PlanningActivityResponse: pid = os.getpid() locality = get_locality() @@ -173,7 +172,7 @@ def unsuccessful( return unsuccessful(PlanningActivityResult.Rejected, str(e)) step_name = "performing unknown operation" - notes: Optional[str] = None + notes: str | None = None try: step_name = "checking F3548-21 operational intent" try: @@ -226,7 +225,7 @@ def unsuccessful( @webapp.route("/scdsc/v1/flights/", methods=["DELETE"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) -def scdsc_delete_flight(flight_id: str) -> Tuple[str, int]: +def scdsc_delete_flight(flight_id: str) -> tuple[str, int]: """Implements flight deletion in SCD automated testing injection API.""" del_resp, status_code = delete_flight(flight_id) @@ -251,7 +250,7 @@ def scdsc_delete_flight(flight_id: str) -> Tuple[str, int]: return flask.jsonify(resp), status_code -def delete_flight(flight_id) -> Tuple[PlanningActivityResponse, int]: +def delete_flight(flight_id) -> tuple[PlanningActivityResponse, int]: pid = os.getpid() def log(msg: str): @@ -274,11 +273,11 @@ def unsuccessful(msg: str) -> PlanningActivityResponse: ) if flight is None: - return unsuccessful("Flight {} does not exist".format(flight_id)), 404 + return unsuccessful(f"Flight {flight_id} does not exist"), 404 # Delete operational intent from DSS step_name = "performing unknown operation" - notes: Optional[str] = None + notes: str | None = None try: step_name = f"deleting operational intent {flight.op_intent.reference.id} with OVN {flight.op_intent.reference.ovn} from DSS" log(step_name) @@ -326,14 +325,14 @@ def unsuccessful(msg: str) -> PlanningActivityResponse: @webapp.route("/scdsc/v1/clear_area_requests", methods=["POST"]) @requires_scope(SCOPE_SCD_QUALIFIER_INJECT) @idempotent_request() -def scdsc_clear_area() -> Tuple[str, int]: +def scdsc_clear_area() -> tuple[str, int]: try: json = flask.request.json if json is None: raise ValueError("Request did not contain a JSON payload") req: ClearAreaRequest = ImplicitDict.parse(json, ClearAreaRequest) except ValueError as e: - msg = "Unable to parse ClearAreaRequest JSON request: {}".format(e) + msg = f"Unable to parse ClearAreaRequest JSON request: {e}" return msg, 400 clear_resp = clear_area(Volume4D.from_interuss_scd_api(req.extent)) @@ -351,12 +350,12 @@ def scdsc_clear_area() -> Tuple[str, int]: def clear_area(extent: Volume4D) -> ClearAreaResponse: - flights_deleted: List[FlightID] = [] - flight_deletion_errors: Dict[FlightID, dict] = {} - op_intents_removed: List[f3548v21.EntityOVN] = [] - op_intent_removal_errors: Dict[f3548v21.EntityOVN, dict] = {} + flights_deleted: list[FlightID] = [] + flight_deletion_errors: dict[FlightID, dict] = {} + op_intents_removed: list[f3548v21.EntityOVN] = [] + op_intent_removal_errors: dict[f3548v21.EntityOVN, dict] = {} - def make_result(error: Optional[dict] = None) -> ClearAreaResponse: + def make_result(error: dict | None = None) -> ClearAreaResponse: resp = ClearAreaResponse( flights_deleted=flights_deleted, flight_deletion_errors=flight_deletion_errors, diff --git a/monitoring/mock_uss/server.py b/monitoring/mock_uss/server.py index 28ac908ee0..1800d1eb2d 100644 --- a/monitoring/mock_uss/server.py +++ b/monitoring/mock_uss/server.py @@ -1,11 +1,11 @@ import os import signal import time +from collections.abc import Callable from dataclasses import dataclass from datetime import UTC, datetime, timedelta from enum import Enum from multiprocessing import Process -from typing import Callable, Dict, Optional, Tuple import arrow import flask @@ -25,20 +25,20 @@ class TaskTrigger(str, Enum): @dataclass -class OneTimeServerTask(object): +class OneTimeServerTask: run: Callable[[], None] trigger: TaskTrigger @dataclass -class PeriodicServerTask(object): +class PeriodicServerTask: run: Callable[[], None] class MockUSS(flask.Flask): _pid: int - _one_time_tasks: Dict[str, OneTimeServerTask] - _periodic_tasks: Dict[str, PeriodicServerTask] + _one_time_tasks: dict[str, OneTimeServerTask] + _periodic_tasks: dict[str, PeriodicServerTask] jinja_loader = FileSystemLoader( [ @@ -52,7 +52,7 @@ def __init__(self, *args, **kwargs): logger.info(f"Initializing MockUSS from process {self._pid}") self._one_time_tasks = {} self._periodic_tasks = {} - super(MockUSS, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def add_one_time_task( self, task: Callable[[], None], name: str, trigger: TaskTrigger @@ -86,7 +86,7 @@ def shutdown_task_decorator(func): return shutdown_task_decorator def _run_one_time_tasks(self, trigger: TaskTrigger): - tasks: Dict[str, OneTimeServerTask] = {} + tasks: dict[str, OneTimeServerTask] = {} with db as tx: for task_name, task in self._one_time_tasks.items(): if task.trigger == trigger and task_name not in tx.one_time_tasks: @@ -143,7 +143,7 @@ def periodic_task_decorator(func): return periodic_task_decorator - def set_task_period(self, task_name: str, period: Optional[timedelta]): + def set_task_period(self, task_name: str, period: timedelta | None): if task_name not in self._periodic_tasks: raise ValueError( f"Periodic task '{task_name}' is not declared, so its period cannot be set" @@ -183,9 +183,9 @@ def _periodic_tasks_daemon_loop(self): break # Find the earliest scheduled task - earliest_task: Optional[ - Tuple[str, datetime, PeriodicTaskStatus] - ] = None + earliest_task: tuple[str, datetime, PeriodicTaskStatus] | None = ( + None + ) for task_name, task in tx.periodic_tasks.items(): if task.executing: # Don't consider executing tasks that are already executing @@ -285,7 +285,7 @@ def stop(self): f"Process {os.getpid()} detected that server shutdown was already in process when stop was requested" ) - def shutdown(self, signal_number: Optional[int], stack): + def shutdown(self, signal_number: int | None, stack): if os.getpid() != self._pid: logger.debug(f"Process {os.getpid()} skipping shutdown procedure") return diff --git a/monitoring/mock_uss/tracer/context.py b/monitoring/mock_uss/tracer/context.py index a063561b44..dfe19e08df 100644 --- a/monitoring/mock_uss/tracer/context.py +++ b/monitoring/mock_uss/tracer/context.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - import yaml from implicitdict import StringBasedDateTime from yaml.representer import Representer @@ -22,10 +20,10 @@ yaml.add_representer(StringBasedDateTime, Representer.represent_str) -scd_cache: Dict[ObservationAreaID, Dict[str, scd.FetchedEntity]] = {} +scd_cache: dict[ObservationAreaID, dict[str, scd.FetchedEntity]] = {} -def _get_tracer_logger() -> Optional[Logger]: +def _get_tracer_logger() -> Logger | None: kml_server = webapp.config[KEY_TRACER_KML_SERVER] kml_folder = webapp.config[KEY_TRACER_KML_FOLDER] output_folder = webapp.config[KEY_TRACER_OUTPUT_FOLDER] @@ -44,10 +42,10 @@ def _get_tracer_logger() -> Optional[Logger]: tracer_logger: Logger = _get_tracer_logger() -_adapters: Dict[AuthSpec, AuthAdapter] = {} +_adapters: dict[AuthSpec, AuthAdapter] = {} -def resolve_auth_spec(requested_auth_spec: Optional[AuthSpec]) -> AuthSpec: +def resolve_auth_spec(requested_auth_spec: AuthSpec | None) -> AuthSpec: if not requested_auth_spec: if KEY_AUTH_SPEC not in webapp.config or not webapp.config[KEY_AUTH_SPEC]: raise ValueError( diff --git a/monitoring/mock_uss/tracer/database.py b/monitoring/mock_uss/tracer/database.py index fe7fcb11f5..208286256e 100644 --- a/monitoring/mock_uss/tracer/database.py +++ b/monitoring/mock_uss/tracer/database.py @@ -1,6 +1,5 @@ import json from datetime import timedelta -from typing import Dict from implicitdict import ImplicitDict, StringBasedTimeDelta @@ -23,7 +22,7 @@ class Database(ImplicitDict): cleanup_initiated: bool = False """True only when a process has already initiated cleanup""" - observation_areas: Dict[ObservationAreaID, ObservationArea] + observation_areas: dict[ObservationAreaID, ObservationArea] """Set of active observation areas, keyed by ID""" polling_interval: StringBasedTimeDelta = StringBasedTimeDelta(timedelta(seconds=15)) diff --git a/monitoring/mock_uss/tracer/diff.py b/monitoring/mock_uss/tracer/diff.py index 62b3f38904..06502d61fb 100644 --- a/monitoring/mock_uss/tracer/diff.py +++ b/monitoring/mock_uss/tracer/diff.py @@ -1,10 +1,8 @@ -from typing import Optional - from monitoring.monitorlib import formatting from monitoring.monitorlib.fetch import rid, scd, summarize -def isa_diff_text(a: Optional[rid.FetchedISAs], b: Optional[rid.FetchedISAs]) -> str: +def isa_diff_text(a: rid.FetchedISAs | None, b: rid.FetchedISAs | None) -> str: """Create text to display to a real-time user describing a change in ISAs.""" a_summary = summarize.isas(a) if a else {} a_summary = summarize.limit_long_arrays(a_summary, 6) @@ -19,7 +17,7 @@ def isa_diff_text(a: Optional[rid.FetchedISAs], b: Optional[rid.FetchedISAs]) -> def entity_diff_text( - a: Optional[scd.FetchedEntities], b: Optional[scd.FetchedEntities] + a: scd.FetchedEntities | None, b: scd.FetchedEntities | None ) -> str: """Create text to display to a real-time user describing a change in Entities.""" entity_type = ( diff --git a/monitoring/mock_uss/tracer/kml.py b/monitoring/mock_uss/tracer/kml.py index 585bea8917..3d8873b524 100644 --- a/monitoring/mock_uss/tracer/kml.py +++ b/monitoring/mock_uss/tracer/kml.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from datetime import UTC, datetime, timedelta from enum import Enum -from typing import Dict, List, Optional, Protocol, Type +from typing import Protocol import yaml from implicitdict import ImplicitDict @@ -32,8 +32,8 @@ from monitoring.monitorlib.temporal import Time -class Stopwatch(object): - _start_time: Optional[datetime] = None +class Stopwatch: + _start_time: datetime | None = None elapsed_time: timedelta = timedelta(seconds=0) def __enter__(self): @@ -48,7 +48,7 @@ class VolumeType(str, Enum): @dataclass -class HistoricalVolumesCollection(object): +class HistoricalVolumesCollection: type: VolumeType name: str version: str @@ -61,8 +61,8 @@ class HistoricalVolumesRenderer(Protocol): def __call__( self, log_entry: TracerLogEntry, - existing_volume_collections: List[HistoricalVolumesCollection], - ) -> List[HistoricalVolumesCollection]: + existing_volume_collections: list[HistoricalVolumesCollection], + ) -> list[HistoricalVolumesCollection]: """Function that generates named collections of 4D volumes from a tracer log entry. Args: @@ -74,17 +74,17 @@ def __call__( @dataclass -class HistoricalVolumesRenderInfo(object): +class HistoricalVolumesRenderInfo: renderer: HistoricalVolumesRenderer - log_entry_type: Type[TracerLogEntry] + log_entry_type: type[TracerLogEntry] -_historical_volumes_renderers: Dict[ - Type[TracerLogEntry], HistoricalVolumesRenderInfo +_historical_volumes_renderers: dict[ + type[TracerLogEntry], HistoricalVolumesRenderInfo ] = {} -def historical_volumes_renderer(log_entry_type: Type[TracerLogEntry]): +def historical_volumes_renderer(log_entry_type: type[TracerLogEntry]): """Decorator to label a function that renders historical volumes for a tracer log entry. Decorated functions should follow the HistoricalVolumesRenderer Protocol. @@ -123,8 +123,8 @@ def _op_intent_volumes(op_intent: OperationalIntent) -> Volume4DCollection: @historical_volumes_renderer(OperationalIntentNotification) def _historical_volumes_op_intent_notification( log_entry: OperationalIntentNotification, - existing_volume_collections: List[HistoricalVolumesCollection], -) -> List[HistoricalVolumesCollection]: + existing_volume_collections: list[HistoricalVolumesCollection], +) -> list[HistoricalVolumesCollection]: try: req = ImplicitDict.parse( log_entry.request.json, PutOperationalIntentDetailsParameters @@ -175,9 +175,9 @@ def _historical_volumes_op_intent_notification( @historical_volumes_renderer(PollOperationalIntents) def _historical_volumes_op_intent_poll( log_entry: PollOperationalIntents, - existing_volume_collections: List[HistoricalVolumesCollection], -) -> List[HistoricalVolumesCollection]: - hvcs: List[HistoricalVolumesCollection] = [] + existing_volume_collections: list[HistoricalVolumesCollection], +) -> list[HistoricalVolumesCollection]: + hvcs: list[HistoricalVolumesCollection] = [] current_op_intents = set() # Add newly-polled operational intents @@ -258,18 +258,18 @@ def _historical_volumes_op_intent_poll( @dataclass -class StyledVolume(object): +class StyledVolume: name: str volume: Volume4D style: str @dataclass -class VolumesFolder(object): +class VolumesFolder: name: str - volumes: List[StyledVolume] - children: List[VolumesFolder] - reference_time: Optional[Time] = None + volumes: list[StyledVolume] + children: list[VolumesFolder] + reference_time: Time | None = None def truncate(self, latest_time: Time) -> None: to_remove = [] @@ -319,7 +319,7 @@ def render_historical_kml(log_folder: str) -> str: generation_time = Stopwatch() rendering_time = Stopwatch() - historical_volume_collections: List[HistoricalVolumesCollection] = [] + historical_volume_collections: list[HistoricalVolumesCollection] = [] log_files = glob.glob(os.path.join(log_folder, "*.yaml")) log_files.sort() for log_file in log_files: @@ -353,7 +353,7 @@ def render_historical_kml(log_folder: str) -> str: continue # Render log entry into historical volume collections - with open(log_file, "r") as f: + with open(log_file) as f: try: with loading_time: content = yaml.load(f, Loader=yaml.CLoader) @@ -373,7 +373,7 @@ def render_historical_kml(log_folder: str) -> str: # Render historical volume collections into a folder structure with generation_time: - top_folder: Dict[VolumeType, VolumesFolder] = {} + top_folder: dict[VolumeType, VolumesFolder] = {} for hvc in historical_volume_collections: if hvc.type not in top_folder: top_folder[hvc.type] = VolumesFolder( diff --git a/monitoring/mock_uss/tracer/log_types.py b/monitoring/mock_uss/tracer/log_types.py index 587e3ea9b7..f7a5ef33a4 100644 --- a/monitoring/mock_uss/tracer/log_types.py +++ b/monitoring/mock_uss/tracer/log_types.py @@ -2,7 +2,6 @@ import sys from abc import abstractmethod -from typing import Optional, Type from implicitdict import ImplicitDict, StringBasedDateTime @@ -29,7 +28,7 @@ class TracerLogEntry(ImplicitDict): def __init__(self, *args, **kwargs): kwargs = kwargs.copy() kwargs["object_type"] = type(self).__name__ - super(TracerLogEntry, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) @staticmethod @abstractmethod @@ -44,7 +43,7 @@ def human_info(self) -> dict: return self @staticmethod - def entry_type(type_name: Optional[str]) -> Optional[Type]: + def entry_type(type_name: str | None) -> type | None: matches = [ cls for name, cls in sys.modules[__name__].__dict__.items() @@ -61,7 +60,7 @@ def entry_type(type_name: Optional[str]) -> Optional[Type]: return matches[0] @staticmethod - def entry_type_from_prefix(prefix_code: str) -> Optional[Type[TracerLogEntry]]: + def entry_type_from_prefix(prefix_code: str) -> type[TracerLogEntry] | None: matches = [ cls for name, cls in sys.modules[__name__].__dict__.items() @@ -125,7 +124,7 @@ def prefix_code() -> str: existing_subscription: rid_fetch.FetchedSubscription """Subscription, as read from the DSS just before deletion.""" - deleted_subscription: Optional[rid_mutate.ChangedSubscription] + deleted_subscription: rid_mutate.ChangedSubscription | None """Subscription returned from DSS upon deletion.""" @@ -215,7 +214,7 @@ def prefix_code() -> str: existing_subscription: scd_fetch.FetchedSubscription """Subscription, as read from the DSS just before deletion.""" - deleted_subscription: Optional[scd_mutate.MutatedSubscription] + deleted_subscription: scd_mutate.MutatedSubscription | None """Subscription returned from DSS upon deletion.""" @@ -271,7 +270,7 @@ class ObservationAreaImportError(TracerLogEntry): def prefix_code() -> str: return "import_obs_areas_error" - rid_subscriptions: Optional[rid_fetch.FetchedSubscriptions] + rid_subscriptions: rid_fetch.FetchedSubscriptions | None """Result of attempting to fetch RID subscriptions""" diff --git a/monitoring/mock_uss/tracer/observation_areas.py b/monitoring/mock_uss/tracer/observation_areas.py index 3694484669..dc11b4da1a 100644 --- a/monitoring/mock_uss/tracer/observation_areas.py +++ b/monitoring/mock_uss/tracer/observation_areas.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from implicitdict import ImplicitDict from monitoring.monitorlib.geotemporal import Volume4D @@ -47,7 +45,7 @@ class F3548ObservationArea(ImplicitDict): poll: bool """This area observes by periodically polling for information.""" - subscription_id: Optional[str] = None + subscription_id: str | None = None """The F3548 subscription ID established to provide observation via notifications.""" @@ -60,10 +58,10 @@ class ObservationArea(ImplicitDict): area: Volume4D """Spatial-temporal area being observed.""" - f3411: Optional[F3411ObservationArea] = None + f3411: F3411ObservationArea | None = None """How F3411 information is being observed (or not observed, if not specified).""" - f3548: Optional[F3548ObservationArea] = None + f3548: F3548ObservationArea | None = None """How F3548 information is being observed (or not observed, if not specified).""" @property @@ -75,12 +73,12 @@ def polls(self) -> bool: class F3411ObservationAreaRequest(ImplicitDict): """How to observe F3411 activity.""" - auth_spec: Optional[AuthSpec] = None + auth_spec: AuthSpec | None = None """If specified, use this auth spec when performing observation activities. If not specified or blank, use auth spec provided on the command line.""" - dss_base_url: Optional[str] = None + dss_base_url: str | None = None """If specified, use the DSS at this base URL when performing relevant observation activities. If not specified or blank, use DSS URL provided on the command line.""" @@ -98,12 +96,12 @@ class F3411ObservationAreaRequest(ImplicitDict): class F3548ObservationAreaRequest(ImplicitDict): """How to observe F3548 activity.""" - auth_spec: Optional[AuthSpec] = None + auth_spec: AuthSpec | None = None """If specified, use this auth spec when performing observation activities. If not specified or blank, use auth spec provided on the command line.""" - dss_base_url: Optional[str] = None + dss_base_url: str | None = None """If specified, use the DSS at this base URL when performing relevant observation activities. If not specified or blank, use DSS URL provided on the command line.""" @@ -127,10 +125,10 @@ class ObservationAreaRequest(ImplicitDict): area: Volume4D """Spatial-temporal area that should be observed.""" - f3411: Optional[F3411ObservationAreaRequest] = None + f3411: F3411ObservationAreaRequest | None = None """How to observe F3411 (NetRID) activity.""" - f3548: Optional[F3548ObservationAreaRequest] = None + f3548: F3548ObservationAreaRequest | None = None """How to observe F3548 (strategic coordination, conformance monitoring, and constraints) activity.""" @property @@ -142,7 +140,7 @@ def polls(self) -> bool: class ListObservationAreasResponse(ImplicitDict): """Response to list observation areas.""" - areas: List[ObservationArea] + areas: list[ObservationArea] """Observation areas that exist in the system.""" @@ -166,7 +164,7 @@ class ImportObservationAreasRequest(ImplicitDict): area: Volume4D """Spatial-temporal area containing subscriptions to be imported.""" - f3411: Optional[RIDVersion] = None + f3411: RIDVersion | None = None """If specified, search for subscriptions using this F3411 version.""" f3548: bool = False diff --git a/monitoring/mock_uss/tracer/routes/__init__.py b/monitoring/mock_uss/tracer/routes/__init__.py index dc74983907..e88190dc50 100644 --- a/monitoring/mock_uss/tracer/routes/__init__.py +++ b/monitoring/mock_uss/tracer/routes/__init__.py @@ -1,5 +1,4 @@ import os -from typing import Tuple import arrow import flask @@ -20,11 +19,11 @@ @webapp.route("/tracer/status") def tracer_status(): logger.debug(f"Handling tracer_status from {os.getpid()}") - return "Tracer ok {}".format(versioning.get_code_version()) + return f"Tracer ok {versioning.get_code_version()}" @webapp.route("/tracer/", methods=["GET", "PUT", "POST", "DELETE"]) -def tracer_catch_all(u_path) -> Tuple[str, int]: +def tracer_catch_all(u_path) -> tuple[str, int]: logger.debug(f"Handling tracer_catch_all from {os.getpid()}") req = fetch.describe_flask_request(flask.request) log_name = context.tracer_logger.log_new( @@ -34,6 +33,6 @@ def tracer_catch_all(u_path) -> Tuple[str, int]: claims = req.token owner = claims.get("sub", "") label = colored("Bad route", "red") - logger.error("{} to {} ({}): {}".format(label, u_path, owner, log_name)) + logger.error(f"{label} to {u_path} ({owner}): {log_name}") return f"Path is not a supported endpoint: {u_path}", 404 diff --git a/monitoring/mock_uss/tracer/routes/observation_areas.py b/monitoring/mock_uss/tracer/routes/observation_areas.py index 355c530329..de7d03fa88 100644 --- a/monitoring/mock_uss/tracer/routes/observation_areas.py +++ b/monitoring/mock_uss/tracer/routes/observation_areas.py @@ -1,7 +1,6 @@ import os import uuid from datetime import UTC, datetime -from typing import List, Tuple, Union import arrow import flask @@ -50,7 +49,7 @@ def tracer_list_observation_areas() -> flask.Response: @ui_auth.login_required(role="admin") def tracer_upsert_observation_area( area_id: str, -) -> Union[Tuple[str, int], flask.Response]: +) -> tuple[str, int] | flask.Response: try: req_body = flask.request.json if req_body is None: @@ -60,7 +59,7 @@ def tracer_upsert_observation_area( req_body, PutObservationAreaRequest ) except ValueError as e: - msg = "Upsert observation area for tracer unable to parse JSON: {}".format(e) + msg = f"Upsert observation area for tracer unable to parse JSON: {e}" return msg, 400 with db as tx: @@ -91,7 +90,7 @@ def tracer_upsert_observation_area( @ui_auth.login_required(role="admin") def tracer_delete_observation_area( area_id: str, -) -> Union[Tuple[str, int], flask.Response]: +) -> tuple[str, int] | flask.Response: with db as tx: if area_id not in tx.observation_areas: return "Specified observation area not in system", 404 @@ -108,7 +107,7 @@ def tracer_delete_observation_area( @webapp.route("/tracer/observation_areas/import_requests", methods=["POST"]) @ui_auth.login_required(role="admin") -def tracer_import_observation_areas() -> Union[Tuple[str, int], flask.Response]: +def tracer_import_observation_areas() -> tuple[str, int] | flask.Response: try: req_body = flask.request.json if req_body is None: @@ -118,7 +117,7 @@ def tracer_import_observation_areas() -> Union[Tuple[str, int], flask.Response]: req_body, ImportObservationAreasRequest ) except ValueError as e: - msg = "Import observation area for tracer unable to parse JSON: {}".format(e) + msg = f"Import observation area for tracer unable to parse JSON: {e}" return msg, 400 auth_spec = context.resolve_auth_spec(None) @@ -219,7 +218,7 @@ def _shutdown(): ) with db as tx: - observation_areas: List[ObservationArea] = [v for _, v in tx.observation_areas] + observation_areas: list[ObservationArea] = [v for _, v in tx.observation_areas] tx.observation_areas.clear() for area in observation_areas: diff --git a/monitoring/mock_uss/tracer/routes/rid.py b/monitoring/mock_uss/tracer/routes/rid.py index 768614c6de..f3030d6ae7 100644 --- a/monitoring/mock_uss/tracer/routes/rid.py +++ b/monitoring/mock_uss/tracer/routes/rid.py @@ -1,5 +1,4 @@ import os -from typing import Tuple import arrow import flask @@ -29,7 +28,7 @@ ) def tracer_rid_isa_notification_v19( observation_area_id: str, isa_id: str -) -> Tuple[str, int]: +) -> tuple[str, int]: return tracer_rid_isa_notification(isa_id, observation_area_id, RIDVersion.f3411_19) @@ -39,7 +38,7 @@ def tracer_rid_isa_notification_v19( ) def tracer_rid_isa_notification_v22a( observation_area_id: str, isa_id: str -) -> Tuple[str, int]: +) -> tuple[str, int]: return tracer_rid_isa_notification( isa_id, observation_area_id, RIDVersion.f3411_22a ) @@ -47,7 +46,7 @@ def tracer_rid_isa_notification_v22a( def tracer_rid_isa_notification( isa_id: str, observation_area_id: str, rid_version: RIDVersion -) -> Tuple[str, int]: +) -> tuple[str, int]: """Implements RID ISA notification receiver.""" logger.debug(f"Handling tracer_rid_isa_notification from {os.getpid()}") req = fetch.describe_flask_request(flask.request) diff --git a/monitoring/mock_uss/tracer/routes/scd.py b/monitoring/mock_uss/tracer/routes/scd.py index 31d2c38c4a..02e419888e 100644 --- a/monitoring/mock_uss/tracer/routes/scd.py +++ b/monitoring/mock_uss/tracer/routes/scd.py @@ -1,5 +1,4 @@ import os -from typing import Tuple import arrow import flask @@ -23,7 +22,7 @@ "/tracer/f3548v21//uss/v1/operational_intents", methods=["POST"], ) -def tracer_scd_v21_operation_notification(observation_area_id: str) -> Tuple[str, int]: +def tracer_scd_v21_operation_notification(observation_area_id: str) -> tuple[str, int]: """Implements SCD Operation notification receiver.""" logger.debug(f"Handling tracer_scd_v21_operation_notification from {os.getpid()}") req = fetch.describe_flask_request(flask.request) @@ -50,7 +49,7 @@ def tracer_scd_v21_operation_notification(observation_area_id: str) -> Tuple[str op_ref = op["reference"] owner_body = op_ref.get("owner") if owner_body and owner_body != owner: - owner = "{} token|{} body".format(owner, owner_body) + owner = f"{owner} token|{owner_body} body" version = op_ref.get("version", version) ovn = op_ref.get("ovn", ovn) time_range = _print_time_range( @@ -65,24 +64,12 @@ def tracer_scd_v21_operation_notification(observation_area_id: str) -> Tuple[str priority = op_details.get("priority", 0) priority_text = str(priority) logger.info( - "{} {} {} {} v{} ({}) OVN[{}] updated{} -> {}".format( - label, - state, - priority_text, - id, - version, - owner, - ovn, - time_range, - log_name, - ) + f"{label} {state} {priority_text} {id} v{version} ({owner}) OVN[{ovn}] updated{time_range} -> {log_name}" ) else: - logger.info("{} {} ({}) deleted -> {}".format(label, id, owner, log_name)) + logger.info(f"{label} {id} ({owner}) deleted -> {log_name}") except ValueError as e: - logger.error( - "{} ({}) unable to decode JSON: {} -> {}".format(label, owner, e, log_name) - ) + logger.error(f"{label} ({owner}) unable to decode JSON: {e} -> {log_name}") return RESULT @@ -90,7 +77,7 @@ def tracer_scd_v21_operation_notification(observation_area_id: str) -> Tuple[str @webapp.route( "/tracer/f3548v21//uss/v1/constraints", methods=["POST"] ) -def tracer_scd_v21_constraint_notification(observation_area_id: str) -> Tuple[str, int]: +def tracer_scd_v21_constraint_notification(observation_area_id: str) -> tuple[str, int]: """Implements SCD Constraint notification receiver.""" logger.debug(f"Handling tracer_scd_v21_constraint_notification from {os.getpid()}") req = fetch.describe_flask_request(flask.request) @@ -117,7 +104,7 @@ def tracer_scd_v21_constraint_notification(observation_area_id: str) -> Tuple[st constraint_ref = constraint["reference"] owner_body = constraint_ref.get("owner") if owner_body and owner_body != owner: - owner = "{} token|{} body".format(owner, owner_body) + owner = f"{owner} token|{owner_body} body" version = constraint_ref.get("version", version) ovn = constraint_ref.get("ovn", ovn) time_range = _print_time_range( @@ -129,15 +116,11 @@ def tracer_scd_v21_constraint_notification(observation_area_id: str) -> Tuple[st constraint_details = constraint["details"] type = constraint_details.get("type") logger.info( - "{} {} {} v{} ({}) OVN[{}] updated{} -> {}".format( - label, type, id, version, owner, ovn, time_range, log_name - ) + f"{label} {type} {id} v{version} ({owner}) OVN[{ovn}] updated{time_range} -> {log_name}" ) else: - logger.info("{} {} ({}) deleted -> {}".format(label, id, owner, log_name)) + logger.info(f"{label} {id} ({owner}) deleted -> {log_name}") except ValueError as e: - logger.error( - "{} ({}) unable to decode JSON: {} -> {}".format(label, owner, e, log_name) - ) + logger.error(f"{label} ({owner}) unable to decode JSON: {e} -> {log_name}") return RESULT diff --git a/monitoring/mock_uss/tracer/routes/ui.py b/monitoring/mock_uss/tracer/routes/ui.py index d4c1e282b5..426549c9e1 100644 --- a/monitoring/mock_uss/tracer/routes/ui.py +++ b/monitoring/mock_uss/tracer/routes/ui.py @@ -60,7 +60,7 @@ def tracer_download_logs(): zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file: for log in logs: - with open(os.path.join(context.tracer_logger.log_path, log), "r") as f: + with open(os.path.join(context.tracer_logger.log_path, log)) as f: zip_file.writestr(log, f.read()) zip_name = ( f"logs_{datetime.datetime.now(datetime.UTC).isoformat().split('.')[0]}.zip" @@ -115,7 +115,7 @@ def tracer_logs(log): logfile = os.path.join(context.tracer_logger.log_path, log) if not os.path.exists(logfile): flask.abort(404) - with open(logfile, "r") as f: + with open(logfile) as f: objs = [obj for obj in yaml.full_load_all(f)] if len(objs) == 1: obj = objs[0] diff --git a/monitoring/mock_uss/tracer/subscriptions.py b/monitoring/mock_uss/tracer/subscriptions.py index 7f41de9320..a13debfc64 100644 --- a/monitoring/mock_uss/tracer/subscriptions.py +++ b/monitoring/mock_uss/tracer/subscriptions.py @@ -28,7 +28,7 @@ class SubscriptionManagementError(RuntimeError): def __init__(self, msg): - super(SubscriptionManagementError, self).__init__(msg) + super().__init__(msg) def subscribe_rid( @@ -105,7 +105,7 @@ def subscribe_scd( ) if not create_result.success: raise SubscriptionManagementError( - "Could not create new SCD Subscription -> {}".format(logfile) + f"Could not create new SCD Subscription -> {logfile}" ) return subscription_id @@ -123,7 +123,7 @@ def unsubscribe_rid( ) ) raise SubscriptionManagementError( - "Could not query existing RID Subscription -> {}".format(logfile) + f"Could not query existing RID Subscription -> {logfile}" ) if existing_result.subscription is not None: @@ -142,7 +142,7 @@ def unsubscribe_rid( ) if not del_result.success: raise SubscriptionManagementError( - "Could not delete existing RID Subscription -> {}".format(logfile) + f"Could not delete existing RID Subscription -> {logfile}" ) @@ -157,7 +157,7 @@ def unsubscribe_scd(subscription_id: str, scd_client: UTMClientSession) -> None: ) ) raise SubscriptionManagementError( - "Could not query existing SCD Subscription -> {}".format(logfile) + f"Could not query existing SCD Subscription -> {logfile}" ) if get_result.subscription is not None: @@ -175,5 +175,5 @@ def unsubscribe_scd(subscription_id: str, scd_client: UTMClientSession) -> None: ) if not del_result.success: raise SubscriptionManagementError( - "Could not delete existing SCD Subscription -> {}".format(logfile) + f"Could not delete existing SCD Subscription -> {logfile}" ) diff --git a/monitoring/mock_uss/tracer/template.py b/monitoring/mock_uss/tracer/template.py index 043ff130dd..12e6348e40 100644 --- a/monitoring/mock_uss/tracer/template.py +++ b/monitoring/mock_uss/tracer/template.py @@ -10,8 +10,6 @@ def _print_time_range(t0: str, t1: str) -> str: try: t0dt = arrow.get(t0) - now t1dt = arrow.get(t1) - now - return " {} to {}".format( - formatting.format_timedelta(t0dt), formatting.format_timedelta(t1dt) - ) + return f" {formatting.format_timedelta(t0dt)} to {formatting.format_timedelta(t1dt)}" except ValueError: return "" diff --git a/monitoring/mock_uss/tracer/tracer_poll.py b/monitoring/mock_uss/tracer/tracer_poll.py index 861bc7ec07..9b1ee16dc0 100755 --- a/monitoring/mock_uss/tracer/tracer_poll.py +++ b/monitoring/mock_uss/tracer/tracer_poll.py @@ -1,7 +1,6 @@ import datetime import json import sys -from typing import Dict, Optional import arrow from implicitdict import ImplicitDict, StringBasedDateTime @@ -47,9 +46,9 @@ class PollingStatus(ImplicitDict): class PollingValues(ImplicitDict): need_line_break: bool = False - last_isa_result: Optional[FetchedISAs] = None - last_ops_result: Optional[FetchedEntities] = None - last_constraints_result: Optional[FetchedEntities] = None + last_isa_result: FetchedISAs | None = None + last_ops_result: FetchedEntities | None = None + last_constraints_result: FetchedEntities | None = None polling_values = SynchronizedValue( @@ -85,7 +84,7 @@ def _log_poll_start(logger): def poll_observation_areas() -> None: logger = context.tracer_logger _log_poll_start(logger) - observation_areas: Dict[ObservationAreaID, ObservationArea] = ( + observation_areas: dict[ObservationAreaID, ObservationArea] = ( db.value.observation_areas ) for observation_area in observation_areas.values(): @@ -148,7 +147,7 @@ def poll_ops( box = make_latlng_rect(area.area.volume) t0 = datetime.datetime.now(datetime.UTC) if "operational_intents" not in context.scd_cache: - context.scd_cache["operational_intents"]: Dict[ + context.scd_cache["operational_intents"]: dict[ str, fetch.scd.FetchedEntity ] = {} result = fetch.scd.operations( @@ -193,7 +192,7 @@ def poll_constraints( box = make_latlng_rect(area.area.volume) t0 = datetime.datetime.now(datetime.UTC) if "constraints" not in context.scd_cache: - context.scd_cache["constraints"]: Dict[str, fetch.scd.FetchedEntity] = {} + context.scd_cache["constraints"]: dict[str, fetch.scd.FetchedEntity] = {} result = fetch.scd.constraints( scd_client, box, diff --git a/monitoring/mock_uss/tracer/tracerlog.py b/monitoring/mock_uss/tracer/tracerlog.py index 6e8615a20b..8535974818 100644 --- a/monitoring/mock_uss/tracer/tracerlog.py +++ b/monitoring/mock_uss/tracer/tracerlog.py @@ -8,7 +8,7 @@ from monitoring.monitorlib import infrastructure -class Logger(object): +class Logger: def __init__( self, log_path: str, kml_session: infrastructure.KMLGenerationSession = None ): @@ -28,7 +28,7 @@ def log_new(self, content: TracerLogEntry) -> str: basename = "{:06d}_{}_{}".format( n, datetime.datetime.now().strftime("%H%M%S_%f"), content.prefix_code() ) - logname = "{}.yaml".format(basename) + logname = f"{basename}.yaml" fullname = os.path.join(self.log_path, logname) dump = json.loads(json.dumps(content)) @@ -39,7 +39,7 @@ def log_new(self, content: TracerLogEntry) -> str: if self.kml_session: kml_server_filename = os.path.join(self.kml_session.kml_folder, logname) try: - with open(fullname, "r") as f: + with open(fullname) as f: resp = self.kml_session.post( "/realtime_kml", data={"path": self.kml_session.kml_folder}, @@ -48,11 +48,9 @@ def log_new(self, content: TracerLogEntry) -> str: resp.raise_for_status() kml_path = os.path.join(self.log_path, "kml") os.makedirs(kml_path, exist_ok=True) - with open(os.path.join(kml_path, "{}.kml".format(basename)), "w") as f: + with open(os.path.join(kml_path, f"{basename}.kml"), "w") as f: f.write(resp.content.decode("utf-8")) - except IOError as e: - print( - "Error posting {} to KML server: {}".format(kml_server_filename, e) - ) + except OSError as e: + print(f"Error posting {kml_server_filename} to KML server: {e}") return logname diff --git a/monitoring/mock_uss/ui/auth.py b/monitoring/mock_uss/ui/auth.py index a46567e7e6..259868b51f 100644 --- a/monitoring/mock_uss/ui/auth.py +++ b/monitoring/mock_uss/ui/auth.py @@ -1,7 +1,6 @@ import json from dataclasses import dataclass from functools import wraps -from typing import List, Optional import flask import flask_login @@ -24,7 +23,7 @@ GOOGLE_DISCOVERY_URL = "https://accounts.google.com/.well-known/openid-configuration" -def _get_oauth_client() -> Optional[WebApplicationClient]: +def _get_oauth_client() -> WebApplicationClient | None: client_id = webapp.config.get(KEY_GOOGLE_OAUTH_CLIENT_ID) client_secret = webapp.config.get(KEY_GOOGLE_OAUTH_CLIENT_SECRET) if client_id and client_secret: @@ -52,8 +51,8 @@ def _get_oauth_client() -> Optional[WebApplicationClient]: @dataclass class User(flask_login.UserMixin): username: str - password_hash: Optional[str] - roles: List[str] + password_hash: str | None + roles: list[str] def get_id(self) -> str: return self.username @@ -62,7 +61,7 @@ def is_admin(self) -> bool: return "admin" in self.roles -def _get_users() -> List[User]: +def _get_users() -> list[User]: users = [] user_strings = webapp.config.get(KEY_UI_USERS).split(";") for user_string in user_strings: @@ -88,7 +87,7 @@ def _get_users() -> List[User]: @login_manager.user_loader -def load_user(user_id: str) -> Optional[User]: +def load_user(user_id: str) -> User | None: users = [u for u in _get_users() if u.username == user_id] if users: return users[0] @@ -178,7 +177,7 @@ def ui_login_callback(): def login_required( - _func=None, *, role: Optional[str] = None, roles: Optional[List[str]] = None + _func=None, *, role: str | None = None, roles: list[str] | None = None ): if role and roles: raise ValueError("Only one of `role` or `roles` may be specified") diff --git a/monitoring/mock_uss/versioning/routes.py b/monitoring/mock_uss/versioning/routes.py index 1f30d216b8..b26942dbb6 100644 --- a/monitoring/mock_uss/versioning/routes.py +++ b/monitoring/mock_uss/versioning/routes.py @@ -1,5 +1,3 @@ -from typing import Tuple - import flask from uas_standards.interuss.automated_testing.versioning import api, constants @@ -10,7 +8,7 @@ @webapp.route("/versioning/versions/", methods=["GET"]) @requires_scope(constants.Scope.ReadSystemVersions) -def versioning_get_version(system_identity: str) -> Tuple[str, int]: +def versioning_get_version(system_identity: str) -> tuple[str, int]: version = versioning.get_code_version() return flask.jsonify( api.GetVersionResponse( diff --git a/monitoring/monitorlib/auth.py b/monitoring/monitorlib/auth.py index a7f1d072f7..a2b8d2d032 100644 --- a/monitoring/monitorlib/auth.py +++ b/monitoring/monitorlib/auth.py @@ -4,7 +4,7 @@ import re import urllib.parse import uuid -from typing import Any, Dict, List, Optional, Tuple +from typing import Any import cryptography.exceptions import cryptography.hazmat.backends @@ -35,32 +35,32 @@ class NoAuth(AuthAdapter): # This is the private key from test-certs/auth2.key. dummy_private_key = jwcrypto.jwk.JWK.from_pem( - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIICWwIBAAKBgHkNtpy3GB0YTCl2VCCd22i0rJwIGBSazD4QRKvH6rch0IP4igb+\n" - "02r7t0X//tuj0VbwtJz3cEICP8OGSqrdTSCGj5Y03Oa2gPkx/0c0V8D0eSXS/CUC\n" - "0qrYHnAGLqko7eW87HW0rh7nnl2bB4Lu+R8fOmQt5frCJ5eTkzwK5YczAgMBAAEC\n" - "gYAtSgMjGKEt6XQ9IucQmN6Iiuf1LFYOB2gYZC+88PuQblc7uJWzTk08vlXwG3l3\n" - "JQ/h7gY0n6JhH8RJW4m96TO8TrlHLx5aVcW8E//CtgayMn3vBgXida3wvIlAXT8G\n" - "WezsNsWorXLVmz5yov0glu+TIk31iWB5DMs4xXhXdH/t8QJBALQzvF+y5bZEhZin\n" - "qTXkiKqMsKsJbXjP1Sp/3t52VnYVfbxN3CCb7yDU9kg5QwNa3ungE3cXXNMUr067\n" - "9zIraekCQQCr+NSeWAXIEutWewPIykYMQilVtiJH4oFfoEpxvecVv7ulw6kM+Jsb\n" - "o6Pi7x86tMVkwOCzZzy/Uyo/gSHnEZq7AkEAm0hBuU2VuTzOyr8fhvtJ8X2O97QG\n" - "C6c8j4Tk7lqXIuZeFRga6la091vMZmxBnPB/SpX28BbHvHUEpBpBZ5AVkQJAX7Lq\n" - "7urg3MPafpeaNYSKkovG4NGoJgSgJgzXIJCjJfE6hTZqvrMh7bGUo9aZtFugdT74\n" - "TB2pKncnTYuYyDN9vQJACDVr+wvYYA2VdnA9k+/1IyGc1HHd2npQqY9EduCeOGO8\n" - "rXQedG6rirVOF6ypkefIayc3usipVvfadpqcS5ERhw==\n" - "-----END RSA PRIVATE KEY-----".encode("UTF-8") + b"-----BEGIN RSA PRIVATE KEY-----\n" + b"MIICWwIBAAKBgHkNtpy3GB0YTCl2VCCd22i0rJwIGBSazD4QRKvH6rch0IP4igb+\n" + b"02r7t0X//tuj0VbwtJz3cEICP8OGSqrdTSCGj5Y03Oa2gPkx/0c0V8D0eSXS/CUC\n" + b"0qrYHnAGLqko7eW87HW0rh7nnl2bB4Lu+R8fOmQt5frCJ5eTkzwK5YczAgMBAAEC\n" + b"gYAtSgMjGKEt6XQ9IucQmN6Iiuf1LFYOB2gYZC+88PuQblc7uJWzTk08vlXwG3l3\n" + b"JQ/h7gY0n6JhH8RJW4m96TO8TrlHLx5aVcW8E//CtgayMn3vBgXida3wvIlAXT8G\n" + b"WezsNsWorXLVmz5yov0glu+TIk31iWB5DMs4xXhXdH/t8QJBALQzvF+y5bZEhZin\n" + b"qTXkiKqMsKsJbXjP1Sp/3t52VnYVfbxN3CCb7yDU9kg5QwNa3ungE3cXXNMUr067\n" + b"9zIraekCQQCr+NSeWAXIEutWewPIykYMQilVtiJH4oFfoEpxvecVv7ulw6kM+Jsb\n" + b"o6Pi7x86tMVkwOCzZzy/Uyo/gSHnEZq7AkEAm0hBuU2VuTzOyr8fhvtJ8X2O97QG\n" + b"C6c8j4Tk7lqXIuZeFRga6la091vMZmxBnPB/SpX28BbHvHUEpBpBZ5AVkQJAX7Lq\n" + b"7urg3MPafpeaNYSKkovG4NGoJgSgJgzXIJCjJfE6hTZqvrMh7bGUo9aZtFugdT74\n" + b"TB2pKncnTYuYyDN9vQJACDVr+wvYYA2VdnA9k+/1IyGc1HHd2npQqY9EduCeOGO8\n" + b"rXQedG6rirVOF6ypkefIayc3usipVvfadpqcS5ERhw==\n" + b"-----END RSA PRIVATE KEY-----" ) EXPIRATION = 3600 # seconds - def __init__(self, sub: str = "uss_noauth", aud_override: Optional[str] = None): + def __init__(self, sub: str = "uss_noauth", aud_override: str | None = None): super().__init__() self.sub = sub self._aud_override = aud_override # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: timestamp = int( (datetime.datetime.now(datetime.UTC) - _UNIX_EPOCH).total_seconds() ) @@ -94,41 +94,41 @@ class InvalidTokenSignatureAuth(AuthAdapter): # A random RSA2048 private key unknown_private_key = jwcrypto.jwk.JWK.from_pem( - "-----BEGIN PRIVATE KEY-----\n" - "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsa9ZYfRoeA1A5\n" - "oUY38payXpOQzjFUVmm2CUh2WxY1HV3d2/0nkWcjHSD5wIKRCqhBQ3T3rj1AJm4f\n" - "mUasH0dAurutgggTDTxpX4XiskYdG8NuZfyQxtRMGBivFnbySi3A0FwOWZPW3ZXz\n" - "RC2r27URC1IvZc2cpSkbNngK9OUodbxX/pbFU6ltkPbyztcLgdnAcC/R7JfUUmgm\n" - "kmBg9ZTyCFB1gsX3Bgx2YSBGyjLejfTUBcySoJuYFPobSKxBpO1r3S0XWCnz4WOu\n" - "ho5asoqy23ucI5VXXcOSaNVIBVnJVhFyCum04m8E2BKUegJfsRU3DMmVA2kaZaTL\n" - "rcPqDbPzAgMBAAECggEAC5ATyM1i8f5Q4/x/xAK9vmp/ROe/ASPmZPHMbTuAisFU\n" - "aSt2l6+1lfI/IuCZIPbw/6dxcaa6rtGk8vOJfMOAOMQND/63YeeyVHK2fNRtxUf2\n" - "XDH0tRTQaeX3yc4c3fTBiruuYLv7IR6tDqpU0cCjLOhwc4NFPasJzaxicoGn2IWo\n" - "kItqGCBn9qz1Qpxe+GZq4Yzebja2czac0Y4khsvmDcWKuFvaX6rU58iiLEekaHeH\n" - "lu288MKYNUqdQ63HNhWhsAm+abVArAgcl2zWPwf5ex6jCyBxsPMRfoCUwjuJzI28\n" - "3AgOTrwnbmdrjEiF+2UVreTSHoBnVyMfGhAKZ1a0OQKBgQDZoANU/8jQZBSS4tdL\n" - "z5RIBa0pQEvV3mrB4n7rMvSytiYkCTHdD8HUT5PRSSF0fbqF6E3c7Fpq/4wP+Fhv\n" - "32+qJR/Y6uW6dMOwWMGAH5NV0+rvHaqCKRVugSvPx2DzZ7pJ0rEpfdCagc8mfIub\n" - "f0UBlnp13QVeNEfDPhJdk7HZ7QKBgQDK0z4ljFBNpRImdgn5gssR72697cSZim8E\n" - "F/wJinn9dHdCgUB/2hcpB4y2yzomHJAYZVI+I4jT0DryRi0NnlRDljkUXaC4dvlR\n" - "RboB8sPYBJ6GrtlAxBxFXYnzsIE3Xqozgt9LNQELhhKJNKSz4qG4AR4fGhwblaY5\n" - "ycTYXWaJXwKBgARXD5nzW/Lj/BEN2xNU+XUSP+jRsnF6dRCWzscsBftGbK5NTKRG\n" - "+yubxqvm1Hb5Ru4CuwLL5+W4YPe0kTbx8s0m3mK6FIjKaVir/HfsqUiN6GKKaesc\n" - "nKPOiawkIsfX6rwsKoJUUwOx0QrIcxRPznWApcKR/NhrHH9FTqJ1HpflAoGANp1B\n" - "G703lmC/jWm1b+E/Kxos2KmgibOUBycqL6uBA7WLs3W4V3TzTZIB2urIQqDoUBlg\n" - "Vukcm+RzKu+ojAU5LWXTAt/fOiyXH8JFvuaOw6kiwqNsTps//ZGdZuf9M1qjO/Ge\n" - "jNK98EtuzFFHlESPRUvPv5I5RVg7hU4GWjh0NsMCgYAqVoB5x4+ugae+eZgSLfwG\n" - "YWSOiHQhEqqLWAp3MHbuwDVNnpy1KNWh7A/f8Hd3xgPKQIGCl74dBQ+6pv08Dxyt\n" - "az+aNXi/CmaEb3v6abaKdF7uNJCpKUnYJ3lpLrrd9HsLbhszIhs1iPbJK38SDEm0\n" - "xgbwpLAv3YW+usS9x8LAPw==\n" - "-----END PRIVATE KEY-----".encode("UTF-8") + b"-----BEGIN PRIVATE KEY-----\n" + b"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsa9ZYfRoeA1A5\n" + b"oUY38payXpOQzjFUVmm2CUh2WxY1HV3d2/0nkWcjHSD5wIKRCqhBQ3T3rj1AJm4f\n" + b"mUasH0dAurutgggTDTxpX4XiskYdG8NuZfyQxtRMGBivFnbySi3A0FwOWZPW3ZXz\n" + b"RC2r27URC1IvZc2cpSkbNngK9OUodbxX/pbFU6ltkPbyztcLgdnAcC/R7JfUUmgm\n" + b"kmBg9ZTyCFB1gsX3Bgx2YSBGyjLejfTUBcySoJuYFPobSKxBpO1r3S0XWCnz4WOu\n" + b"ho5asoqy23ucI5VXXcOSaNVIBVnJVhFyCum04m8E2BKUegJfsRU3DMmVA2kaZaTL\n" + b"rcPqDbPzAgMBAAECggEAC5ATyM1i8f5Q4/x/xAK9vmp/ROe/ASPmZPHMbTuAisFU\n" + b"aSt2l6+1lfI/IuCZIPbw/6dxcaa6rtGk8vOJfMOAOMQND/63YeeyVHK2fNRtxUf2\n" + b"XDH0tRTQaeX3yc4c3fTBiruuYLv7IR6tDqpU0cCjLOhwc4NFPasJzaxicoGn2IWo\n" + b"kItqGCBn9qz1Qpxe+GZq4Yzebja2czac0Y4khsvmDcWKuFvaX6rU58iiLEekaHeH\n" + b"lu288MKYNUqdQ63HNhWhsAm+abVArAgcl2zWPwf5ex6jCyBxsPMRfoCUwjuJzI28\n" + b"3AgOTrwnbmdrjEiF+2UVreTSHoBnVyMfGhAKZ1a0OQKBgQDZoANU/8jQZBSS4tdL\n" + b"z5RIBa0pQEvV3mrB4n7rMvSytiYkCTHdD8HUT5PRSSF0fbqF6E3c7Fpq/4wP+Fhv\n" + b"32+qJR/Y6uW6dMOwWMGAH5NV0+rvHaqCKRVugSvPx2DzZ7pJ0rEpfdCagc8mfIub\n" + b"f0UBlnp13QVeNEfDPhJdk7HZ7QKBgQDK0z4ljFBNpRImdgn5gssR72697cSZim8E\n" + b"F/wJinn9dHdCgUB/2hcpB4y2yzomHJAYZVI+I4jT0DryRi0NnlRDljkUXaC4dvlR\n" + b"RboB8sPYBJ6GrtlAxBxFXYnzsIE3Xqozgt9LNQELhhKJNKSz4qG4AR4fGhwblaY5\n" + b"ycTYXWaJXwKBgARXD5nzW/Lj/BEN2xNU+XUSP+jRsnF6dRCWzscsBftGbK5NTKRG\n" + b"+yubxqvm1Hb5Ru4CuwLL5+W4YPe0kTbx8s0m3mK6FIjKaVir/HfsqUiN6GKKaesc\n" + b"nKPOiawkIsfX6rwsKoJUUwOx0QrIcxRPznWApcKR/NhrHH9FTqJ1HpflAoGANp1B\n" + b"G703lmC/jWm1b+E/Kxos2KmgibOUBycqL6uBA7WLs3W4V3TzTZIB2urIQqDoUBlg\n" + b"Vukcm+RzKu+ojAU5LWXTAt/fOiyXH8JFvuaOw6kiwqNsTps//ZGdZuf9M1qjO/Ge\n" + b"jNK98EtuzFFHlESPRUvPv5I5RVg7hU4GWjh0NsMCgYAqVoB5x4+ugae+eZgSLfwG\n" + b"YWSOiHQhEqqLWAp3MHbuwDVNnpy1KNWh7A/f8Hd3xgPKQIGCl74dBQ+6pv08Dxyt\n" + b"az+aNXi/CmaEb3v6abaKdF7uNJCpKUnYJ3lpLrrd9HsLbhszIhs1iPbJK38SDEm0\n" + b"xgbwpLAv3YW+usS9x8LAPw==\n" + b"-----END PRIVATE KEY-----" ) def __init__(self, sub: str = "uss_unsigned"): super().__init__() self.sub = sub - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: timestamp = int( (datetime.datetime.now(datetime.UTC) - _UNIX_EPOCH).total_seconds() ) @@ -162,7 +162,7 @@ def __init__(self, token_endpoint: str, sub: str): self._oauth_session = requests.Session() # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: url = "{}?grant_type=client_credentials&scope={}&intended_audience={}&issuer=dummy&sub={}".format( self._oauth_token_endpoint, urllib.parse.quote(" ".join(scopes)), @@ -190,7 +190,7 @@ def __init__( self._oauth_token_endpoint = token_endpoint self._oauth_session = session - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: url = "{}?grant_type=client_credentials&scope={}&intended_audience={}".format( self._oauth_token_endpoint, urllib.parse.quote(" ".join(scopes)), @@ -220,7 +220,7 @@ def __init__(self, token_endpoint: str, service_account_json: str): self._session_issuer = _SessionIssuer(token_endpoint, oauth_session) # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: return self._session_issuer.issue_token(intended_audience, scopes) @@ -248,7 +248,7 @@ def __init__(self, token_endpoint: str, target_service_account: str): self._session_issuer = _SessionIssuer(token_endpoint, oauth_session) # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: return self._session_issuer.issue_token(intended_audience, scopes) @@ -266,8 +266,8 @@ def __init__( self._client_id = client_id # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: - scopes.append("aud:{}".format(intended_audience)) + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: + scopes.append(f"aud:{intended_audience}") response = requests.post( self._oauth_token_endpoint, data={ @@ -289,7 +289,7 @@ def issue_token(self, intended_audience: str, scopes: List[str]) -> str: def _load_keypair( key_path: str, cert_url: str, backend: Any -) -> Tuple[jwcrypto.jwk.JWK, jwcrypto.jwk.JWK]: +) -> tuple[jwcrypto.jwk.JWK, jwcrypto.jwk.JWK]: # Retrieve certificate to validate match with private key response = requests.get(cert_url) assert response.status_code == 200 @@ -305,7 +305,7 @@ def _load_keypair( ) # Generate public key directly from private key - with open(key_path, "r") as f: + with open(key_path) as f: key_content = f.read().encode("utf-8") if key_path[-4:].lower() == ".key" or key_path[-4:].lower() == ".pem": private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key( @@ -330,7 +330,7 @@ def _load_keypair( def _make_jws( - token_headers: Dict[str, str], + token_headers: dict[str, str], payload: str, private_jwk: jwcrypto.jwk.JWK, public_jwk: jwcrypto.jwk.JWK, @@ -374,7 +374,7 @@ def __init__( client_id: str, key_path: str, cert_url: str, - key_id: Optional[str] = None, + key_id: str | None = None, signature_style: str = "UPP2", ): """Create an AuthAdapter that retrieves tokens via message signing. @@ -402,9 +402,7 @@ def __init__( self._signature_style = signature_style if signature_style not in ("UPP2", "UFT"): raise ValueError( - "signature_style must be either `UPP2` or `UFT`; found `{}`".format( - signature_style - ) + f"signature_style must be either `UPP2` or `UFT`; found `{signature_style}`" ) self._private_jwk, self._public_jwk = _load_keypair( @@ -418,7 +416,7 @@ def __init__( self._kid = self._public_jwk.thumbprint() # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: # Construct request body query = { "grant_type": "client_credentials", @@ -438,7 +436,7 @@ def issue_token(self, intended_audience: str, scopes: List[str]) -> str: } # Add signature header(s) and associated information - request_headers: Dict[str, str] = {} + request_headers: dict[str, str] = {} if self._signature_style == "UPP2": signature = _make_jws( token_headers, payload, self._private_jwk, self._public_jwk @@ -467,18 +465,18 @@ def issue_token(self, intended_audience: str, scopes: List[str]) -> str: "@query": "?", "authorization": "", "content-type": "application/x-www-form-urlencoded", - "content-digest": "sha-512=:{}:".format(content_digest), + "content-digest": f"sha-512=:{content_digest}:", "x-utm-jws-header": ", ".join( - '{}="{}"'.format(k, v) for k, v in token_headers.items() + f'{k}="{v}"' for k, v in token_headers.items() ), "@signature-params": "({});created={}".format( - " ".join('"{}"'.format(c) for c in components), + " ".join(f'"{c}"' for c in components), int(datetime.datetime.now(datetime.UTC).timestamp()), ), } components.append("@signature-params") signature_base = "\n".join( - '"{}": {}'.format(c, signature_content[c]) for c in components + f'"{c}": {signature_content[c]}' for c in components ) signature = _make_signature( signature_base, self._private_jwk, self._public_jwk @@ -488,7 +486,7 @@ def issue_token(self, intended_audience: str, scopes: List[str]) -> str: if k[0] != "@": request_headers[k] = v request_headers["x-utm-message-signature"] = ( - "utm-message-signature=:{}:".format(signature) + f"utm-message-signature=:{signature}:" ) request_headers["x-utm-message-signature-input"] = ( "utm-message-signature={}".format( @@ -529,7 +527,7 @@ def __init__( self._send_request_as_data = send_request_as_data # Overrides method in AuthAdapter - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: payload = { "grant_type": "client_credentials", "client_id": self._client_id, @@ -561,16 +559,14 @@ def __init__( ): send_request_as_data = send_request_as_data.lower() == "true" - super(FlightPassport, self).__init__( - token_endpoint, client_id, client_secret, send_request_as_data - ) + super().__init__(token_endpoint, client_id, client_secret, send_request_as_data) self._send_request_as_data = send_request_as_data class AccessTokenError(RuntimeError): def __init__(self, msg): - super(AccessTokenError, self).__init__(msg) + super().__init__(msg) def all_subclasses(cls): @@ -604,7 +600,7 @@ def make_auth_adapter(spec: AuthSpec) -> AuthAdapter: adapter_name = m.group(1) adapter_classes = {cls.__name__: cls for cls in all_subclasses(AuthAdapter)} if adapter_name not in adapter_classes: - raise ValueError("Auth adapter `%s` does not exist" % adapter_name) + raise ValueError(f"Auth adapter `{adapter_name}` does not exist") Adapter = adapter_classes[adapter_name] adapter_param_string = m.group(2) diff --git a/monitoring/monitorlib/auth_validation.py b/monitoring/monitorlib/auth_validation.py index 92c693504b..c066cfd55e 100644 --- a/monitoring/monitorlib/auth_validation.py +++ b/monitoring/monitorlib/auth_validation.py @@ -1,6 +1,6 @@ import json from functools import wraps -from typing import List, NamedTuple +from typing import NamedTuple import flask import jwcrypto.jwk @@ -10,7 +10,7 @@ class Authorization(NamedTuple): client_id: str - scopes: List[str] + scopes: list[str] issuer: str @@ -111,7 +111,7 @@ def wrapper(*args, **kwargs): raise InvalidAccessTokenError("Access token cannot be decoded.") except jwt.InvalidTokenError as e: raise InvalidAccessTokenError( - "Unexpected InvalidTokenError: %s" % str(e) + f"Unexpected InvalidTokenError: {str(e)}" ) issuer = r.get("iss", None) flask.request.jwt = Authorization( @@ -140,8 +140,8 @@ def fix_key(public_key: str) -> str: public_key = jwk.export_to_pem().decode("utf-8") else: public_key = resp.content.decode("utf-8") - elif public_key.startswith("/") or public_key.endswith((".pem")): - with open(public_key, "r") as f: + elif public_key.startswith("/") or public_key.endswith(".pem"): + with open(public_key) as f: public_key = f.read() # ENV variables sometimes don't pass newlines, spec says white space # doesn't matter, but pyjwt cares about it, so fix it diff --git a/monitoring/monitorlib/clients/__init__.py b/monitoring/monitorlib/clients/__init__.py index 7a00b334bf..243422e89b 100644 --- a/monitoring/monitorlib/clients/__init__.py +++ b/monitoring/monitorlib/clients/__init__.py @@ -1,5 +1,4 @@ from abc import ABC -from typing import List from monitoring.monitorlib.fetch import Query @@ -14,7 +13,7 @@ def on_query(self, query: Query) -> None: raise NotImplementedError("QueryHook subclass did not implement on_query") -query_hooks: List[QueryHook] = [] +query_hooks: list[QueryHook] = [] def call_query_hooks(query: Query) -> None: diff --git a/monitoring/monitorlib/clients/flight_planning/client.py b/monitoring/monitorlib/clients/flight_planning/client.py index 735a404dce..88d937aa4b 100644 --- a/monitoring/monitorlib/clients/flight_planning/client.py +++ b/monitoring/monitorlib/clients/flight_planning/client.py @@ -1,5 +1,4 @@ from abc import ABC, abstractmethod -from typing import Optional, Set from monitoring.monitorlib.clients.flight_planning.flight_info import ( ExecutionStyle, @@ -25,11 +24,11 @@ class FlightPlannerClient(ABC): """Client to interact with a USS as a user performing flight planning activities and as the test director preparing for tests involving flight planning activities.""" participant_id: ParticipantID - created_flight_ids: Set[FlightID] + created_flight_ids: set[FlightID] def __init__(self, participant_id: ParticipantID): self.participant_id = participant_id - self.created_flight_ids: Set[FlightID] = set() + self.created_flight_ids: set[FlightID] = set() # ===== Emulation of user actions ===== @@ -38,7 +37,7 @@ def try_plan_flight( self, flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Instruct the USS to emulate a normal user trying to plan the described flight. @@ -53,7 +52,7 @@ def try_update_flight( flight_id: FlightID, updated_flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Instruct the USS to emulate a normal user trying to update the specified flight as described. diff --git a/monitoring/monitorlib/clients/flight_planning/client_scd.py b/monitoring/monitorlib/clients/flight_planning/client_scd.py index d8a3a46bc9..c261e68282 100644 --- a/monitoring/monitorlib/clients/flight_planning/client_scd.py +++ b/monitoring/monitorlib/clients/flight_planning/client_scd.py @@ -1,5 +1,4 @@ import uuid -from typing import Dict, Optional from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.scd.v1 import api as scd_api @@ -34,10 +33,10 @@ class SCDFlightPlannerClient(FlightPlannerClient): SCD_SCOPE = scd_api_constants.Scope.Inject _session: UTMClientSession - _plan_statuses: Dict[FlightID, FlightPlanStatus] + _plan_statuses: dict[FlightID, FlightPlanStatus] def __init__(self, session: UTMClientSession, participant_id: ParticipantID): - super(SCDFlightPlannerClient, self).__init__(participant_id=participant_id) + super().__init__(participant_id=participant_id) self._session = session self._plan_statuses = {} @@ -46,7 +45,7 @@ def _inject( flight_id: FlightID, flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: if execution_style != ExecutionStyle.IfAllowed: raise PlanningActivityError( @@ -156,7 +155,7 @@ def try_plan_flight( self, flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: return self._inject( str(uuid.uuid4()), flight_info, execution_style, additional_fields @@ -167,7 +166,7 @@ def try_update_flight( flight_id: FlightID, updated_flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: return self._inject( flight_id, updated_flight_info, execution_style, additional_fields diff --git a/monitoring/monitorlib/clients/flight_planning/client_v1.py b/monitoring/monitorlib/clients/flight_planning/client_v1.py index 484d07d311..698e969c3b 100644 --- a/monitoring/monitorlib/clients/flight_planning/client_v1.py +++ b/monitoring/monitorlib/clients/flight_planning/client_v1.py @@ -1,5 +1,4 @@ import uuid -from typing import Optional from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.flight_planning.v1 import api @@ -32,7 +31,7 @@ class V1FlightPlannerClient(FlightPlannerClient): _session: UTMClientSession def __init__(self, session: UTMClientSession, participant_id: ParticipantID): - super(V1FlightPlannerClient, self).__init__(participant_id=participant_id) + super().__init__(participant_id=participant_id) self._session = session def _inject( @@ -40,7 +39,7 @@ def _inject( flight_plan_id: FlightID, flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: flight_plan = flight_info.to_flight_plan() req = api.UpsertFlightPlanRequest( @@ -111,7 +110,7 @@ def try_plan_flight( self, flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: return self._inject( str(uuid.uuid4()), flight_info, execution_style, additional_fields @@ -122,7 +121,7 @@ def try_update_flight( flight_id: FlightID, updated_flight_info: FlightInfo, execution_style: ExecutionStyle, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: return self._inject( flight_id, updated_flight_info, execution_style, additional_fields diff --git a/monitoring/monitorlib/clients/flight_planning/flight_info.py b/monitoring/monitorlib/clients/flight_planning/flight_info.py index 2d080ea0eb..a2ca876bbf 100644 --- a/monitoring/monitorlib/clients/flight_planning/flight_info.py +++ b/monitoring/monitorlib/clients/flight_planning/flight_info.py @@ -1,7 +1,6 @@ from __future__ import annotations from enum import Enum -from typing import List, Optional from implicitdict import ImplicitDict from uas_standards.ansi_cta_2063_a import SerialNumber @@ -22,7 +21,7 @@ class ASTMF354821OpIntentInformation(ImplicitDict): """Information provided about a flight plan that is necessary for ASTM F3548-21.""" - priority: Optional[Priority] + priority: Priority | None # ===== U-space ===== @@ -74,13 +73,13 @@ class FlightAuthorisationData(ImplicitDict): uas_class: UASClass - identification_technologies: List[str] + identification_technologies: list[str] """Technology used to identify the UAS. Required by ANNEX IV of COMMISSION IMPLEMENTING REGULATION (EU) 2021/664, paragraph 6.""" - uas_type_certificate: Optional[str] + uas_type_certificate: str | None """Provisional field. Not applicable as of September 2021. Required only if `uas_class` is set to `other` by ANNEX IV of COMMISSION IMPLEMENTING REGULATION (EU) 2021/664, paragraph 4.""" - connectivity_methods: List[str] + connectivity_methods: list[str] """Connectivity methods. Required by ANNEX IV of COMMISSION IMPLEMENTING REGULATION (EU) 2021/664, paragraph 7.""" endurance_minutes: int @@ -96,7 +95,7 @@ class FlightAuthorisationData(ImplicitDict): Required by ANNEX IV of COMMISSION IMPLEMENTING REGULATION (EU) 2021/664, paragraph 10. """ - uas_id: Optional[str] + uas_id: str | None """When applicable, the registration number of the unmanned aircraft. This is expressed using the nationality and registration mark of the unmanned aircraft in line with ICAO Annex 7. @@ -147,28 +146,28 @@ class RPAS26FlightDetailsFlightProfile(str, Enum): class RPAS26FlightDetails(ImplicitDict): """Information about a flight necessary to plan successfully using the RPAS Platform Operating Rules version 2.6.""" - operator_type: Optional[RPAS26FlightDetailsOperatorType] + operator_type: RPAS26FlightDetailsOperatorType | None """The type of operator.""" - uas_serial_numbers: Optional[List[str]] + uas_serial_numbers: list[str] | None """The list of UAS/drone serial numbers that will be operated during the operation.""" - uas_registration_numbers: Optional[List[str]] + uas_registration_numbers: list[str] | None """The list of UAS/drone registration numbers that will be operated during the operation.""" - aircraft_type: Optional[RPAS26FlightDetailsAircraftType] + aircraft_type: RPAS26FlightDetailsAircraftType | None """Type of vehicle being used as per ASTM F3411-22a.""" - flight_profile: Optional[RPAS26FlightDetailsFlightProfile] + flight_profile: RPAS26FlightDetailsFlightProfile | None """Type of flight profile.""" - pilot_license_number: Optional[str] + pilot_license_number: str | None """License number for the pilot.""" - pilot_phone_number: Optional[str] + pilot_phone_number: str | None """Contact phone number for the pilot.""" - operator_number: Optional[str] + operator_number: str | None """Operator number.""" @@ -256,13 +255,13 @@ class FlightInfo(ImplicitDict): basic_information: BasicFlightPlanInformation - astm_f3548_21: Optional[ASTMF354821OpIntentInformation] + astm_f3548_21: ASTMF354821OpIntentInformation | None - uspace_flight_authorisation: Optional[FlightAuthorisationData] + uspace_flight_authorisation: FlightAuthorisationData | None - rpas_operating_rules_2_6: Optional[RPAS26FlightDetails] + rpas_operating_rules_2_6: RPAS26FlightDetails | None - additional_information: Optional[dict] + additional_information: dict | None """Any information relevant to a particular jurisdiction or use case not described in the standard schema. The keys and values must be agreed upon between the test designers and USSs under test.""" @staticmethod @@ -449,7 +448,7 @@ class ExecutionStyle(str, Enum): """The user is communicating an actual state of reality. The USS should consider the user to be actually performing (or attempting to perform) this action, regardless of whether or not the action is allowed under relevant UTM rules.""" -def extents_of(flight_infos: List[FlightInfo]) -> Volume4D: +def extents_of(flight_infos: list[FlightInfo]) -> Volume4D: """Return the bounding volume of all volumes in the flight infos""" return sum( [f.basic_information.area for f in flight_infos], Volume4DCollection([]) diff --git a/monitoring/monitorlib/clients/flight_planning/flight_info_template.py b/monitoring/monitorlib/clients/flight_planning/flight_info_template.py index 69c6f2cdbe..6717bc669d 100644 --- a/monitoring/monitorlib/clients/flight_planning/flight_info_template.py +++ b/monitoring/monitorlib/clients/flight_planning/flight_info_template.py @@ -1,5 +1,3 @@ -from typing import Dict, List, Optional - from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.scd.v1 import api as scd_api @@ -32,7 +30,7 @@ class BasicFlightPlanInformationTemplate(ImplicitDict): area: Volume4DTemplateCollection """User intends to or may fly anywhere in this entire area.""" - def resolve(self, times: Dict[TimeDuringTest, Time]) -> BasicFlightPlanInformation: + def resolve(self, times: dict[TimeDuringTest, Time]) -> BasicFlightPlanInformation: kwargs = {k: v for k, v in self.items()} kwargs["area"] = Volume4DCollection([t.resolve(times) for t in self.area]) return ImplicitDict.parse(kwargs, BasicFlightPlanInformation) @@ -43,19 +41,19 @@ class FlightInfoTemplate(ImplicitDict): basic_information: BasicFlightPlanInformationTemplate - astm_f3548_21: Optional[ASTMF354821OpIntentInformation] + astm_f3548_21: ASTMF354821OpIntentInformation | None - uspace_flight_authorisation: Optional[FlightAuthorisationData] + uspace_flight_authorisation: FlightAuthorisationData | None - rpas_operating_rules_2_6: Optional[RPAS26FlightDetails] + rpas_operating_rules_2_6: RPAS26FlightDetails | None - additional_information: Optional[dict] + additional_information: dict | None """Any information relevant to a particular jurisdiction or use case not described in the standard schema. The keys and values must be agreed upon between the test designers and USSs under test.""" - transformations: Optional[List[Transformation]] + transformations: list[Transformation] | None """If specified, transform this flight according to these transformations in order (after all templates are resolved).""" - def resolve(self, times: Dict[TimeDuringTest, Time]) -> FlightInfo: + def resolve(self, times: dict[TimeDuringTest, Time]) -> FlightInfo: kwargs = {k: v for k, v in self.items() if k not in {"transformations"}} basic_info = self.basic_information.resolve(times) if "transformations" in self and self.transformations: @@ -65,7 +63,7 @@ def resolve(self, times: Dict[TimeDuringTest, Time]) -> FlightInfo: return ImplicitDict.parse(kwargs, FlightInfo) def to_scd_inject_request( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ) -> scd_api.InjectFlightRequest: """Render a legacy SCD injection API request object from this object.""" diff --git a/monitoring/monitorlib/clients/flight_planning/planning.py b/monitoring/monitorlib/clients/flight_planning/planning.py index 8395a4844a..9385880672 100644 --- a/monitoring/monitorlib/clients/flight_planning/planning.py +++ b/monitoring/monitorlib/clients/flight_planning/planning.py @@ -1,7 +1,6 @@ from __future__ import annotations from enum import Enum -from typing import Dict, List, Optional from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21 import api as f3548v21 @@ -51,7 +50,7 @@ class FlightPlanStatus(str, Enum): """The flight plan was closed successfully by the USS and is now out of the UTM system.""" @staticmethod - def from_flightinfo(info: Optional[FlightInfo]) -> FlightPlanStatus: + def from_flightinfo(info: FlightInfo | None) -> FlightPlanStatus: if info is None: return FlightPlanStatus.NotPlanned if info.basic_information.uas_state != UasState.Nominal: @@ -78,7 +77,7 @@ class PlanningActivityResponse(ImplicitDict): flight_id: FlightID """Identity of flight for which the planning activity was conducted.""" - queries: List[Query] + queries: list[Query] """Queries used to accomplish this activity.""" activity_result: PlanningActivityResult @@ -87,10 +86,10 @@ class PlanningActivityResponse(ImplicitDict): flight_plan_status: FlightPlanStatus """Status of the flight plan following the flight planning activity.""" - notes: Optional[str] + notes: str | None """Any human-readable notes regarding the activity.""" - includes_advisories: Optional[AdvisoryInclusion] = AdvisoryInclusion.Unknown + includes_advisories: AdvisoryInclusion | None = AdvisoryInclusion.Unknown def to_inject_flight_response(self) -> scd_api.InjectFlightResponse: if self.activity_result == PlanningActivityResult.Completed: @@ -127,19 +126,19 @@ def to_inject_flight_response(self) -> scd_api.InjectFlightResponse: class ClearAreaResponse(ImplicitDict): - flights_deleted: List[FlightID] + flights_deleted: list[FlightID] """List of IDs of flights that were deleted during this area clearing operation.""" - flight_deletion_errors: Dict[FlightID, dict] + flight_deletion_errors: dict[FlightID, dict] """When an error was encountered deleting a particular flight, information about that error.""" - op_intents_removed: List[f3548v21.EntityOVN] + op_intents_removed: list[f3548v21.EntityOVN] """List of IDs of ASTM F3548-21 operational intent references that were removed during this area clearing operation.""" - op_intent_removal_errors: Dict[f3548v21.EntityOVN, dict] + op_intent_removal_errors: dict[f3548v21.EntityOVN, dict] """When an error was encountered removing a particular operational intent reference, information about that error.""" - error: Optional[dict] = None + error: dict | None = None """If an error was encountered that could not be linked to a specific flight or operational intent, information about it will be populated here.""" @property diff --git a/monitoring/monitorlib/clients/flight_planning/test_preparation.py b/monitoring/monitorlib/clients/flight_planning/test_preparation.py index 0bca52bb3e..f9134956a7 100644 --- a/monitoring/monitorlib/clients/flight_planning/test_preparation.py +++ b/monitoring/monitorlib/clients/flight_planning/test_preparation.py @@ -1,15 +1,13 @@ -from typing import List, Optional - from implicitdict import ImplicitDict from monitoring.monitorlib.fetch import Query class ClearAreaOutcome(ImplicitDict): - success: Optional[bool] = False + success: bool | None = False """True if, and only if, all flight plans in the specified area managed by the USS were canceled and removed.""" - message: Optional[str] + message: str | None """If the USS was unable to clear the entire area, this message can provide information on the problem encountered.""" @@ -18,8 +16,8 @@ class ClearAreaResponse(ImplicitDict): class TestPreparationActivityResponse(ImplicitDict): - errors: Optional[List[str]] = None + errors: list[str] | None = None """If any errors occurred during this activity, a list of those errors.""" - queries: List[Query] + queries: list[Query] """Queries used to accomplish this activity.""" diff --git a/monitoring/monitorlib/clients/geospatial_info/client.py b/monitoring/monitorlib/clients/geospatial_info/client.py index 6c19dc22b5..fa4313d390 100644 --- a/monitoring/monitorlib/clients/geospatial_info/client.py +++ b/monitoring/monitorlib/clients/geospatial_info/client.py @@ -1,5 +1,4 @@ from abc import ABC, abstractmethod -from typing import List from monitoring.monitorlib.clients.geospatial_info.querying import ( GeospatialFeatureCheck, @@ -25,7 +24,7 @@ def __init__(self, participant_id: ParticipantID): @abstractmethod def query_geospatial_features( - self, checks: List[GeospatialFeatureCheck] + self, checks: list[GeospatialFeatureCheck] ) -> GeospatialFeatureQueryResponse: """Instruct the USS to emulate a normal user/app trying to check for the specified geospatial information. diff --git a/monitoring/monitorlib/clients/geospatial_info/client_geospatial_map.py b/monitoring/monitorlib/clients/geospatial_info/client_geospatial_map.py index 9114d9f40e..1fd4716e8f 100644 --- a/monitoring/monitorlib/clients/geospatial_info/client_geospatial_map.py +++ b/monitoring/monitorlib/clients/geospatial_info/client_geospatial_map.py @@ -1,5 +1,3 @@ -from typing import List - from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.geospatial_map.v1.api import ( OPERATIONS, @@ -27,11 +25,11 @@ class GeospatialMapClient(GeospatialInfoClient): _session: UTMClientSession def __init__(self, session: UTMClientSession, participant_id: ParticipantID): - super(GeospatialMapClient, self).__init__(participant_id) + super().__init__(participant_id) self._session = session def query_geospatial_features( - self, checks: List[GeospatialFeatureCheck] + self, checks: list[GeospatialFeatureCheck] ) -> GeospatialFeatureQueryResponse: req_checks = [c.to_geospatial_map() for c in checks] diff --git a/monitoring/monitorlib/clients/geospatial_info/querying.py b/monitoring/monitorlib/clients/geospatial_info/querying.py index e80be2d970..7880b67ab2 100644 --- a/monitoring/monitorlib/clients/geospatial_info/querying.py +++ b/monitoring/monitorlib/clients/geospatial_info/querying.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import List, Optional from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.geospatial_map.v1 import ( @@ -28,18 +27,18 @@ class GeospatialFeatureFilter(ImplicitDict): # TODO: Add position - volumes4d: Optional[List[Volume4D]] + volumes4d: list[Volume4D] | None """If specified, only select geospatial features at least partially intersecting one or more of these volumes.""" # TODO: Add after & before - restriction_source: Optional[str] + restriction_source: str | None """If specified, only select geospatial features originating from the named source. The acceptable values for this field will be established by the test designers and will generally be used to limit responses to only the intended datasets under test even when the USS may have more additional geospatial features from other sources that may otherwise be relevant.""" - operation_rule_set: Optional[str] + operation_rule_set: str | None """If specified, only select geospatial features that would be relevant when planning an operation under the specified rule set. The acceptable values for this field will be established by the test designers and will generally correspond to sets of rules under which the system under test plans operations.""" - resulting_operational_impact: Optional[OperationalImpact] + resulting_operational_impact: OperationalImpact | None """If specified, only select geospatial features that would cause the specified outcome if a user attempted to plan a flight applicable to all the other criteria in this filter set.""" def to_geospatial_map(self) -> geospatial_map_api.GeospatialFeatureFilterSet: @@ -60,7 +59,7 @@ def to_geospatial_map(self) -> geospatial_map_api.GeospatialFeatureFilterSet: class GeospatialFeatureCheck(ImplicitDict): - filter_sets: Optional[List[GeospatialFeatureFilter]] + filter_sets: list[GeospatialFeatureFilter] | None """Select geospatial features which match any of the specified filter sets.""" def to_geospatial_map(self) -> geospatial_map_api.GeospatialMapCheck: @@ -89,13 +88,13 @@ class GeospatialFeatureCheckResult(ImplicitDict): features_selection_outcome: SelectionOutcome """Indication of whether one or more applicable geospatial features were selected according to the selection criteria of the corresponding check.""" - message: Optional[str] + message: str | None """A human-readable description of why the unsuccessful `features_selection_outcome` was reported. Should only be populated when appropriate according to the value of the `features_selection_outcome` field.""" class GeospatialFeatureQueryResponse(ImplicitDict): - queries: List[Query] + queries: list[Query] """Queries used to accomplish this activity.""" - results: List[GeospatialFeatureCheckResult] + results: list[GeospatialFeatureCheckResult] """Responses to each of the `checks` in the request. The number of entries in this array should match the number of entries in the `checks` field of the request.""" diff --git a/monitoring/monitorlib/clients/mock_uss/interactions.py b/monitoring/monitorlib/clients/mock_uss/interactions.py index 1e8303cfe1..85e0ceecad 100644 --- a/monitoring/monitorlib/clients/mock_uss/interactions.py +++ b/monitoring/monitorlib/clients/mock_uss/interactions.py @@ -1,6 +1,5 @@ from datetime import datetime from enum import Enum -from typing import List import yaml from implicitdict import ImplicitDict @@ -47,4 +46,4 @@ def interaction_time(self) -> datetime: class ListLogsResponse(ImplicitDict): - interactions: List[Interaction] + interactions: list[Interaction] diff --git a/monitoring/monitorlib/clients/mock_uss/mock_uss_scd_injection_api.py b/monitoring/monitorlib/clients/mock_uss/mock_uss_scd_injection_api.py index b2705c809a..526a157435 100644 --- a/monitoring/monitorlib/clients/mock_uss/mock_uss_scd_injection_api.py +++ b/monitoring/monitorlib/clients/mock_uss/mock_uss_scd_injection_api.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.flight_planning.v1.api import ( UpsertFlightPlanRequest, @@ -21,7 +19,7 @@ class MockUssFlightBehavior(ImplicitDict): } """ - modify_sharing_methods: List[str] + modify_sharing_methods: list[str] """ list of intent sharing http methods GET and POST to be modified""" modify_fields: dict @@ -31,10 +29,10 @@ class MockUssFlightBehavior(ImplicitDict): class MockUSSInjectFlightRequest(InjectFlightRequest): """InjectFlightRequest sent to mock_uss, which looks for the optional additional fields below.""" - behavior: Optional[MockUssFlightBehavior] + behavior: MockUssFlightBehavior | None class MockUSSUpsertFlightPlanRequest(UpsertFlightPlanRequest): """UpsertFlightPlanRequest sent to mock_uss, which looks for the optional additional fields below.""" - behavior: Optional[MockUssFlightBehavior] + behavior: MockUssFlightBehavior | None diff --git a/monitoring/monitorlib/clients/scd.py b/monitoring/monitorlib/clients/scd.py index 169c6cf30e..3a22896021 100644 --- a/monitoring/monitorlib/clients/scd.py +++ b/monitoring/monitorlib/clients/scd.py @@ -1,5 +1,3 @@ -from typing import List, Optional, Tuple - from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21 import api from uas_standards.astm.f3548.v21.api import OperationalIntentState @@ -10,7 +8,7 @@ from monitoring.monitorlib.infrastructure import UTMClientSession -def _scopes_for_state(state: OperationalIntentState) -> List[str]: +def _scopes_for_state(state: OperationalIntentState) -> list[str]: """ The scope required to set an OIR's state depends on the state itself: Contingent and Nonconforming require the CMSA scope in addition to SC. @@ -29,7 +27,7 @@ def _scopes_for_state(state: OperationalIntentState) -> List[str]: def query_operational_intent_references( utm_client: UTMClientSession, area_of_interest: api.Volume4D -) -> List[api.OperationalIntentReference]: +) -> list[api.OperationalIntentReference]: url = "/dss/v1/operational_intent_references/query" subject = f"queryOperationalIntentReferences from {url}" req = api.QueryOperationalIntentReferenceParameters( @@ -65,7 +63,7 @@ def create_operational_intent_reference( id: str, req: api.PutOperationalIntentReferenceParameters, ) -> api.ChangeOperationalIntentReferenceResponse: - url = "/dss/v1/operational_intent_references/{}".format(id) + url = f"/dss/v1/operational_intent_references/{id}" subject = f"createOperationalIntentReference to {url}" query = fetch.query_and_describe( utm_client, "PUT", url, json=req, scopes=_scopes_for_state(req.state) @@ -98,7 +96,7 @@ def update_operational_intent_reference( ovn: str, req: api.PutOperationalIntentReferenceParameters, ) -> api.ChangeOperationalIntentReferenceResponse: - url = "/dss/v1/operational_intent_references/{}/{}".format(id, ovn) + url = f"/dss/v1/operational_intent_references/{id}/{ovn}" subject = f"updateOperationalIntentReference to {url}" query = fetch.query_and_describe( utm_client, "PUT", url, json=req, scopes=_scopes_for_state(req.state) @@ -156,7 +154,7 @@ def delete_operational_intent_reference( def get_operational_intent_details( utm_client: UTMClientSession, uss_base_url: str, id: str -) -> Tuple[api.OperationalIntent, Query]: +) -> tuple[api.OperationalIntent, Query]: url = f"{uss_base_url}/uss/v1/operational_intents/{id}" subject = f"getOperationalIntentDetails from {url}" query = fetch.query_and_describe( @@ -221,8 +219,8 @@ def notify_operational_intent_details_changed( def notify_subscribers( utm_client: UTMClientSession, id: str, - operational_intent: Optional[api.OperationalIntent], - subscribers: List[api.SubscriberToNotify], + operational_intent: api.OperationalIntent | None, + subscribers: list[api.SubscriberToNotify], ): for subscriber in subscribers: kwargs = { diff --git a/monitoring/monitorlib/clients/versioning/client.py b/monitoring/monitorlib/clients/versioning/client.py index b8e1b7193b..77b828ff7f 100644 --- a/monitoring/monitorlib/clients/versioning/client.py +++ b/monitoring/monitorlib/clients/versioning/client.py @@ -1,6 +1,5 @@ from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Optional from monitoring.monitorlib.fetch import Query, QueryError from monitoring.uss_qualifier.configurations.configuration import ParticipantID @@ -11,7 +10,7 @@ class VersionQueryError(QueryError): @dataclass -class GetVersionResponse(object): +class GetVersionResponse: version: str query: Query @@ -25,7 +24,7 @@ def __init__(self, participant_id: ParticipantID): self.participant_id = participant_id @abstractmethod - def get_version(self, version_type: Optional[str]) -> GetVersionResponse: + def get_version(self, version_type: str | None) -> GetVersionResponse: """Retrieve the version of the specified system. Args: diff --git a/monitoring/monitorlib/clients/versioning/client_interuss.py b/monitoring/monitorlib/clients/versioning/client_interuss.py index 9ad33ad5bd..b7abd515a2 100644 --- a/monitoring/monitorlib/clients/versioning/client_interuss.py +++ b/monitoring/monitorlib/clients/versioning/client_interuss.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.versioning import api from uas_standards.interuss.automated_testing.versioning.constants import Scope @@ -16,11 +14,11 @@ class InterUSSVersioningClient(VersioningClient): def __init__(self, session: UTMClientSession, participant_id: ParticipantID): - super(InterUSSVersioningClient, self).__init__(participant_id) + super().__init__(participant_id) self._session = session self._participant_id = participant_id - def get_version(self, version_type: Optional[str]) -> GetVersionResponse: + def get_version(self, version_type: str | None) -> GetVersionResponse: op = api.OPERATIONS[api.OperationID.GetVersion] kwargs = { "client": self._session, diff --git a/monitoring/monitorlib/delay.py b/monitoring/monitorlib/delay.py index 597fa04061..4539e892d3 100644 --- a/monitoring/monitorlib/delay.py +++ b/monitoring/monitorlib/delay.py @@ -1,6 +1,5 @@ import time from datetime import timedelta -from typing import Union from loguru import logger @@ -8,7 +7,7 @@ """Number of seconds to delay above which a reasoning message should be displayed.""" -def sleep(duration: Union[float, timedelta], reason: str) -> None: +def sleep(duration: float | timedelta, reason: str) -> None: """Sleep for the specified amount of time, logging the fact that the delay is occurring (when appropriate). Args: diff --git a/monitoring/monitorlib/deprecation.py b/monitoring/monitorlib/deprecation.py index 44bbab7443..47b21c7c66 100644 --- a/monitoring/monitorlib/deprecation.py +++ b/monitoring/monitorlib/deprecation.py @@ -3,7 +3,6 @@ import os import traceback from dataclasses import dataclass -from typing import List, Optional from loguru import logger @@ -11,12 +10,12 @@ @dataclass -class CallSite(object): +class CallSite: func: str file: str - line: Optional[int] + line: int | None - def __init__(self, func: str, file: str, line: Optional[int] = None): + def __init__(self, func: str, file: str, line: int | None = None): """Define a site that may call a deprecated function. Args: @@ -29,7 +28,7 @@ def __init__(self, func: str, file: str, line: Optional[int] = None): self.line = line @staticmethod - def from_frame_summary(stack: List[traceback.FrameSummary]) -> CallSite: + def from_frame_summary(stack: list[traceback.FrameSummary]) -> CallSite: monitoring_path = os.path.dirname(monitoring.__file__) i = -1 @@ -53,7 +52,7 @@ class DeprecatedUsageError(RuntimeError): pass -def assert_deprecated(legacy_callers: Optional[List[CallSite]] = None) -> None: +def assert_deprecated(legacy_callers: list[CallSite] | None = None) -> None: """Assert that the function calling this function is deprecated. If the calling function is not explicitly listed in legacy_callers, then a DeprecatedUsageError will be thrown. diff --git a/monitoring/monitorlib/dicts.py b/monitoring/monitorlib/dicts.py index a6fda41fbe..6707fec070 100644 --- a/monitoring/monitorlib/dicts.py +++ b/monitoring/monitorlib/dicts.py @@ -1,5 +1,5 @@ import json -from typing import Any, List, Tuple, Union +from typing import Any from implicitdict import ImplicitDict @@ -18,7 +18,7 @@ Example: things[2].foo.bar[0]""" -def get_element(obj: dict, element: Union[JSONAddress, List[str]], pop=False) -> Any: +def get_element(obj: dict, element: JSONAddress | list[str], pop=False) -> Any: """Get a descendant element from obj, optionally popping (removing) it from obj. Args: @@ -63,7 +63,7 @@ def get_element(obj: dict, element: Union[JSONAddress, List[str]], pop=False) -> def get_element_or_default( - obj: dict, element: Union[JSONAddress, List[str]], default_value: Any + obj: dict, element: JSONAddress | list[str], default_value: Any ) -> Any: try: return get_element(obj, element) @@ -79,8 +79,8 @@ class RemovedElement(ImplicitDict): def remove_elements( - src: dict, elements_to_remove: List[JSONAddress] -) -> Tuple[dict, List[RemovedElement]]: + src: dict, elements_to_remove: list[JSONAddress] +) -> tuple[dict, list[RemovedElement]]: """Remove a list of elements from the src dict. Args: @@ -92,7 +92,7 @@ def remove_elements( * A list of RemovedElements containing the values removed. """ less = json.loads(json.dumps(src)) - removed: List[RemovedElement] = [] + removed: list[RemovedElement] = [] for element in elements_to_remove: try: removed_value = get_element(less, element, pop=True) diff --git a/monitoring/monitorlib/fetch/__init__.py b/monitoring/monitorlib/fetch/__init__.py index df62d3f47a..850b8fc1ee 100644 --- a/monitoring/monitorlib/fetch/__init__.py +++ b/monitoring/monitorlib/fetch/__init__.py @@ -5,7 +5,7 @@ import uuid from dataclasses import dataclass from enum import Enum -from typing import Dict, List, Optional, Type, TypeVar, Union +from typing import TypeVar from urllib.parse import urlparse import flask @@ -24,10 +24,10 @@ @dataclass class Settings: - connect_timeout_seconds: Optional[float] = 3.1 + connect_timeout_seconds: float | None = 3.1 """Number of seconds to allow for establishing a connection.""" - read_timeout_seconds: Optional[float] = 6.1 + read_timeout_seconds: float | None = 6.1 """Number of seconds to allow for a request to complete after establishing a connection.""" attempts: int = 2 @@ -47,20 +47,20 @@ class Settings: class RequestDescription(ImplicitDict): method: str url: str - headers: Optional[dict] - json: Optional[dict] = None - body: Optional[str] = None + headers: dict | None + json: dict | None = None + body: str | None = None - initiated_at: Optional[StringBasedDateTime] - received_at: Optional[StringBasedDateTime] + initiated_at: StringBasedDateTime | None + received_at: StringBasedDateTime | None def __init__(self, *args, **kwargs): - super(RequestDescription, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if "headers" not in self: self.headers = {} @property - def token(self) -> Dict: + def token(self) -> dict: return infrastructure.get_token_claims(self.headers) @property @@ -88,7 +88,7 @@ def url_hostname(self) -> str: return urlparse(self.url).hostname @property - def content(self) -> Optional[str]: + def content(self) -> str | None: if self.json is not None: return json.dumps(self.json) else: @@ -139,16 +139,16 @@ def describe_request( class ResponseDescription(ImplicitDict): - code: Optional[int] = None - failure: Optional[str] - headers: Optional[dict] + code: int | None = None + failure: str | None + headers: dict | None elapsed_s: float reported: StringBasedDateTime - json: Optional[dict] = None - body: Optional[str] = None + json: dict | None = None + body: str | None = None def __init__(self, *args, **kwargs): - super(ResponseDescription, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if "headers" not in self: self.headers = {} @@ -157,7 +157,7 @@ def status_code(self) -> int: return self.code or 999 @property - def content(self) -> Optional[str]: + def content(self) -> str | None: if self.json is not None: return json.dumps(self.json) else: @@ -183,7 +183,7 @@ def describe_response(resp: requests.Response) -> ResponseDescription: def describe_aiohttp_response( - status: int, headers: Dict, resp_json: Dict, duration: datetime.timedelta + status: int, headers: dict, resp_json: dict, duration: datetime.timedelta ) -> ResponseDescription: kwargs = { "code": status, @@ -447,10 +447,10 @@ class Query(ImplicitDict): request: RequestDescription response: ResponseDescription - participant_id: Optional[str] + participant_id: str | None """If specified, identifier of the USS/participant hosting the server involved in this query.""" - query_type: Optional[QueryType] + query_type: QueryType | None """If specified, the recognized type of this query.""" @property @@ -464,11 +464,11 @@ def status_code(self) -> int: return self.response.status_code @property - def json_result(self) -> Optional[Dict]: + def json_result(self) -> dict | None: return self.response.json @property - def error_message(self) -> Optional[str]: + def error_message(self) -> str | None: return ( self.json_result["message"] if self.json_result is not None and "message" in self.json_result @@ -476,7 +476,7 @@ def error_message(self) -> Optional[str]: ) @property - def failure_details(self) -> Optional[str]: + def failure_details(self) -> str | None: """ Returns the error message if one is available, otherwise returns the response content. To be used to fill in the details of a check failure. @@ -497,7 +497,7 @@ def get_client_sub(self): ) return payload["sub"] - def parse_json_result(self, parse_type: Type[ResponseType]) -> ResponseType: + def parse_json_result(self, parse_type: type[ResponseType]) -> ResponseType: """Parses the JSON result into the specified type. Args: @@ -521,16 +521,16 @@ class QueryError(RuntimeError): This error will usually wrap one query that failed and that caused the error, and may be accompanied by additional queries for context.""" - queries: List[Query] + queries: list[Query] - def __init__(self, msg: str, queries: Optional[Union[Query, List[Query]]] = None): + def __init__(self, msg: str, queries: Query | list[Query] | None = None): """ Args: msg: description of the error queries: 0, one or multiple queries related to the error. If multiple queries are provided, the first one in the list should be the main cause of the error. """ - super(QueryError, self).__init__(msg) + super().__init__(msg) self.msg = msg if queries is None: self.queries = [] @@ -548,12 +548,12 @@ def cause_status_code(self) -> int: return self.queries[0].status_code @property - def query_timestamps(self) -> List[datetime.datetime]: + def query_timestamps(self) -> list[datetime.datetime]: """Returns the timestamps of all queries present in this QueryError.""" return [q.request.timestamp for q in self.queries] @property - def cause(self) -> Optional[Query]: + def cause(self) -> Query | None: """Returns the query that caused this error.""" if len(self.queries) == 0: return None @@ -572,8 +572,8 @@ def stacktrace(self) -> str: def describe_query( resp: requests.Response, initiated_at: datetime.datetime, - query_type: Optional[QueryType] = None, - participant_id: Optional[str] = None, + query_type: QueryType | None = None, + participant_id: str | None = None, ) -> Query: query = Query( request=describe_request(resp.request, initiated_at), @@ -587,11 +587,11 @@ def describe_query( def query_and_describe( - client: Optional[infrastructure.UTMClientSession], + client: infrastructure.UTMClientSession | None, verb: str, url: str, - query_type: Optional[QueryType] = None, - participant_id: Optional[str] = None, + query_type: QueryType | None = None, + participant_id: str | None = None, expect_failure: bool = False, **kwargs, ) -> Query: diff --git a/monitoring/monitorlib/fetch/evaluation.py b/monitoring/monitorlib/fetch/evaluation.py index ef355ce9f7..71863c6feb 100644 --- a/monitoring/monitorlib/fetch/evaluation.py +++ b/monitoring/monitorlib/fetch/evaluation.py @@ -1,12 +1,11 @@ import statistics from operator import attrgetter -from typing import Dict, List, Tuple from . import Query -def classify_query_by_url(queries: List[Query]) -> Dict[str, List[Query]]: - queries_by_url: Dict[str, List[Query]] = dict() +def classify_query_by_url(queries: list[Query]) -> dict[str, list[Query]]: + queries_by_url: dict[str, list[Query]] = dict() for query in queries: if query.request.url not in queries_by_url: queries_by_url[query.request.url] = list() @@ -15,8 +14,8 @@ def classify_query_by_url(queries: List[Query]) -> Dict[str, List[Query]]: def get_init_subsequent_queries_durations( - min_session_length_sec: int, queries_by_url: Dict[str, List[Query]] -) -> Tuple[List[float], List[float]]: + min_session_length_sec: int, queries_by_url: dict[str, list[Query]] +) -> tuple[list[float], list[float]]: """ Get the initial and subsequent durations of the provided queries. Initial and subsequent queries are identified through 1. their URL and 2. the time elapsed between the queries. @@ -25,8 +24,8 @@ def get_init_subsequent_queries_durations( :return: list of durations of respectively initial and subsequent queries """ - init_durations: List[float] = list() # list of initial queries duration - subsequent_durations: List[float] = list() # list of subsequent queries duration + init_durations: list[float] = list() # list of initial queries duration + subsequent_durations: list[float] = list() # list of subsequent queries duration for queries in queries_by_url.values(): queries.sort(key=attrgetter("request.initiated_at")) # sort queries by time @@ -50,7 +49,7 @@ def get_init_subsequent_queries_durations( return init_durations, subsequent_durations -def compute_percentiles(values: List[float], percentiles: List[int]) -> List[float]: +def compute_percentiles(values: list[float], percentiles: list[int]) -> list[float]: """ Compute percentiles of durations. :param values: list of durations for which to compute the percentiles diff --git a/monitoring/monitorlib/fetch/rid.py b/monitoring/monitorlib/fetch/rid.py index 16b458192f..999873f3fd 100644 --- a/monitoring/monitorlib/fetch/rid.py +++ b/monitoring/monitorlib/fetch/rid.py @@ -1,7 +1,7 @@ from __future__ import annotations import datetime -from typing import Any, Dict, List, Optional, Union +from typing import Any import s2sphere import yaml @@ -24,8 +24,8 @@ class ISA(ImplicitDict): """Version-independent representation of a F3411 identification service area.""" - v19_value: Optional[v19.api.IdentificationServiceArea] = None - v22a_value: Optional[v22a.api.IdentificationServiceArea] = None + v19_value: v19.api.IdentificationServiceArea | None = None + v22a_value: v22a.api.IdentificationServiceArea | None = None @property def rid_version(self) -> RIDVersion: @@ -39,7 +39,7 @@ def rid_version(self) -> RIDVersion: @property def raw( self, - ) -> Union[v19.api.IdentificationServiceArea, v22a.api.IdentificationServiceArea]: + ) -> v19.api.IdentificationServiceArea | v22a.api.IdentificationServiceArea: if self.rid_version == RIDVersion.f3411_19: return self.v19_value elif self.rid_version == RIDVersion.f3411_22a: @@ -125,7 +125,7 @@ def query_flights( session: UTMClientSession, area: s2sphere.LatLngRect, include_recent_positions: bool = True, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedUSSFlights: return uss_flights( self.flights_url, @@ -152,23 +152,23 @@ class Position(ImplicitDict): time: datetime.datetime """Timestamp for the position.""" - height: Optional[RIDHeight] + height: RIDHeight | None - accuracy_v: Optional[ - VerticalAccuracy - ] # Note: we use the enum defined in the v2 API as it is equivalent (and thus compatible) to the v19 one + accuracy_v: ( + VerticalAccuracy | None + ) # Note: we use the enum defined in the v2 API as it is equivalent (and thus compatible) to the v19 one """Vertical error that is likely to be present in this reported position""" - accuracy_h: Optional[ - HorizontalAccuracy - ] # Note: we use the enum defined in the v2 API as it is equivalent (and thus compatible) to the v19 one + accuracy_h: ( + HorizontalAccuracy | None + ) # Note: we use the enum defined in the v2 API as it is equivalent (and thus compatible) to the v19 one """Horizontal error that is likely to be present in this reported position.""" @staticmethod def from_v19_rid_aircraft_position( p: v19.api.RIDAircraftPosition, t: v19.api.StringBasedDateTime, - h: Optional[v19.api.RIDHeight], + h: v19.api.RIDHeight | None, ) -> Position: return Position( lat=p.lat, @@ -198,8 +198,8 @@ def from_v22a_rid_aircraft_position( class Flight(ImplicitDict): """Version-independent representation of a F3411 flight.""" - v19_value: Optional[v19.api.RIDFlight] = None - v22a_value: Optional[v22a.api.RIDFlight] = None + v19_value: v19.api.RIDFlight | None = None + v22a_value: v22a.api.RIDFlight | None = None @property def rid_version(self) -> RIDVersion: @@ -213,7 +213,7 @@ def rid_version(self) -> RIDVersion: @property def raw( self, - ) -> Union[v19.api.RIDFlight, v22a.api.RIDFlight]: + ) -> v19.api.RIDFlight | v22a.api.RIDFlight: if self.rid_version == RIDVersion.f3411_19: return self.v19_value elif self.rid_version == RIDVersion.f3411_22a: @@ -230,7 +230,7 @@ def id(self) -> str: @property def most_recent_position( self, - ) -> Optional[Position]: + ) -> Position | None: if "current_state" in self.raw and self.raw.current_state: if self.rid_version == RIDVersion.f3411_19: return Position.from_v19_rid_aircraft_position( @@ -251,7 +251,7 @@ def most_recent_position( return None @property - def recent_positions(self) -> List[Position]: + def recent_positions(self) -> list[Position]: if self.rid_version == RIDVersion.f3411_19: return [ Position.from_v19_rid_aircraft_position(p.position, p.time, self.height) @@ -268,7 +268,7 @@ def recent_positions(self) -> List[Position]: ) @property - def operational_status(self) -> Optional[str]: + def operational_status(self) -> str | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value( "current_state" @@ -291,7 +291,7 @@ def operational_status(self) -> Optional[str]: ) @property - def track(self) -> Optional[float]: + def track(self) -> float | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value( "current_state" @@ -310,7 +310,7 @@ def track(self) -> Optional[float]: ) @property - def speed(self) -> Optional[float]: + def speed(self) -> float | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value( "current_state" @@ -329,7 +329,7 @@ def speed(self) -> Optional[float]: ) @property - def timestamp(self) -> Optional[StringBasedDateTime]: + def timestamp(self) -> StringBasedDateTime | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("current_state"): return None @@ -344,7 +344,7 @@ def timestamp(self) -> Optional[StringBasedDateTime]: ) @property - def timestamp_accuracy(self) -> Optional[float]: + def timestamp_accuracy(self) -> float | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("current_state"): return None @@ -361,7 +361,7 @@ def timestamp_accuracy(self) -> Optional[float]: @property def speed_accuracy( self, - ) -> Optional[Union[v19.api.SpeedAccuracy, v22a.api.SpeedAccuracy]]: + ) -> v19.api.SpeedAccuracy | v22a.api.SpeedAccuracy | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("current_state"): return None @@ -376,7 +376,7 @@ def speed_accuracy( ) @property - def vertical_speed(self) -> Optional[float]: + def vertical_speed(self) -> float | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("current_state"): return None @@ -393,7 +393,7 @@ def vertical_speed(self) -> Optional[float]: @property def aircraft_type( self, - ) -> Optional[Union[v19.api.RIDAircraftType, v22a.api.UAType]]: + ) -> v19.api.RIDAircraftType | v22a.api.UAType | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("aircraft_type"): return None @@ -410,7 +410,7 @@ def aircraft_type( @property def height( self, - ) -> Optional[Union[v19.api.RIDHeight, v22a.api.RIDHeight]]: + ) -> v19.api.RIDHeight | v22a.api.RIDHeight | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value( "current_state" @@ -429,7 +429,7 @@ def height( f"Cannot retrieve aircraft_type using RID version {self.rid_version}" ) - def errors(self) -> List[str]: + def errors(self) -> list[str]: try: rid_version = self.rid_version except ValueError as e: @@ -488,8 +488,8 @@ def errors(self) -> List[str]: class FlightDetails(ImplicitDict): """Version-independent representation of details for a F3411 flight.""" - v19_value: Optional[v19.api.RIDFlightDetails] = None - v22a_value: Optional[v22a.api.RIDFlightDetails] = None + v19_value: v19.api.RIDFlightDetails | None = None + v22a_value: v22a.api.RIDFlightDetails | None = None @property def rid_version(self) -> RIDVersion: @@ -503,7 +503,7 @@ def rid_version(self) -> RIDVersion: @property def raw( self, - ) -> Union[v19.api.RIDFlightDetails, v22a.api.RIDFlightDetails]: + ) -> v19.api.RIDFlightDetails | v22a.api.RIDFlightDetails: if self.rid_version == RIDVersion.f3411_19: return self.v19_value elif self.rid_version == RIDVersion.f3411_22a: @@ -529,7 +529,7 @@ def operator_id(self) -> str: ) @property - def arbitrary_uas_id(self) -> Optional[str]: + def arbitrary_uas_id(self) -> str | None: """Returns a UAS id as a plain string without type hint. If multiple are provided: For v19, registration_number is returned if set, else it falls back to the serial_number. @@ -560,7 +560,7 @@ def arbitrary_uas_id(self) -> Optional[str]: @property def eu_classification( self, - ) -> Optional[v22a.api.UAClassificationEU]: + ) -> v22a.api.UAClassificationEU | None: if self.rid_version == RIDVersion.f3411_19: return None elif self.rid_version == RIDVersion.f3411_22a: @@ -576,7 +576,7 @@ def eu_classification( @property def operator_location( self, - ) -> Optional[geo.LatLngPoint]: + ) -> geo.LatLngPoint | None: if self.rid_version == RIDVersion.f3411_19: if not self.v19_value.has_field_with_value("operator_location"): return None @@ -597,7 +597,7 @@ def operator_location( @property def operator_altitude( self, - ) -> Optional[geo.Altitude]: + ) -> geo.Altitude | None: if self.rid_version == RIDVersion.f3411_19: return None elif self.rid_version == RIDVersion.f3411_22a: @@ -617,7 +617,7 @@ def operator_altitude( @property def operator_altitude_type( self, - ) -> Optional[str]: + ) -> str | None: if self.rid_version == RIDVersion.f3411_19: return None elif self.rid_version == RIDVersion.f3411_22a: @@ -636,7 +636,7 @@ def operator_altitude_type( @property def serial_number( self, - ) -> Optional[SerialNumber]: + ) -> SerialNumber | None: if self.rid_version == RIDVersion.f3411_19: return self.v19_value.serial_number elif self.rid_version == RIDVersion.f3411_22a: @@ -654,7 +654,7 @@ def serial_number( @property def registration_id( self, - ) -> Optional[SerialNumber]: + ) -> SerialNumber | None: if self.rid_version == RIDVersion.f3411_19: return self.v19_value.registration_number elif self.rid_version == RIDVersion.f3411_22a: @@ -673,11 +673,11 @@ def registration_id( class Subscription(ImplicitDict): """Version-independent representation of a F3411 subscription.""" - v19_value: Optional[v19.api.Subscription] = None - v22a_value: Optional[v22a.api.Subscription] = None + v19_value: v19.api.Subscription | None = None + v22a_value: v22a.api.Subscription | None = None @property - def duration(self) -> Optional[datetime.timedelta]: + def duration(self) -> datetime.timedelta | None: if self.v19_value is not None: if ( self.v19_value.time_end is not None @@ -715,7 +715,7 @@ def rid_version(self) -> RIDVersion: @property def raw( self, - ) -> Union[v19.api.Subscription, v22a.api.Subscription]: + ) -> v19.api.Subscription | v22a.api.Subscription: if self.rid_version == RIDVersion.f3411_19: return self.v19_value elif self.rid_version == RIDVersion.f3411_22a: @@ -793,8 +793,8 @@ def owner(self) -> str: class RIDQuery(ImplicitDict): - v19_query: Optional[Query] = None - v22a_query: Optional[Query] = None + v19_query: Query | None = None + v22a_query: Query | None = None @property def rid_version(self) -> RIDVersion: @@ -825,11 +825,11 @@ def success(self) -> bool: return not self.errors @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: raise NotImplementedError("RIDQuery.errors must be overriden") @property - def participant_id(self) -> Optional[str]: + def participant_id(self) -> str | None: if self.rid_version == RIDVersion.f3411_19: if "participant_id" in self.v19_query: return self.v19_query.participant_id @@ -876,7 +876,7 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code == 404: return ["ISA not present in DSS"] if self.status_code != 200: @@ -909,7 +909,7 @@ def errors(self) -> List[str]: return [] @property - def isa(self) -> Optional[ISA]: + def isa(self) -> ISA | None: if not self.success: return None if self.rid_version == RIDVersion.f3411_19: @@ -927,7 +927,7 @@ def isa( rid_version: RIDVersion, session: UTMClientSession, dss_base_url: str = "", - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedISA: if rid_version == RIDVersion.f3411_19: op = v19.api.OPERATIONS[v19.api.OperationID.GetIdentificationServiceArea] @@ -983,7 +983,7 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: # Overall errors if self.status_code != 200: return [f"Failed to search ISAs in DSS ({self.status_code})"] @@ -1016,7 +1016,7 @@ def errors(self) -> List[str]: return [] @property - def isas(self) -> Dict[str, ISA]: + def isas(self) -> dict[str, ISA]: if not self.success: return {} if self.rid_version == RIDVersion.f3411_19: @@ -1033,7 +1033,7 @@ def isas(self) -> Dict[str, ISA]: ) @property - def flights_urls(self) -> Dict[str, str]: + def flights_urls(self) -> dict[str, str]: """Returns map of flights URL to owning USS""" if not self.success: return {} @@ -1058,13 +1058,13 @@ def has_different_content_than(self, other: Any) -> bool: def isas( - area: List[s2sphere.LatLng], - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + area: list[s2sphere.LatLng], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, rid_version: RIDVersion, session: UTMClientSession, dss_base_url: str = "", - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedISAs: url_time_params = "" if start_time is not None: @@ -1128,9 +1128,9 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code != 200: - return ["Failed to get flights ({})".format(self.status_code)] + return [f"Failed to get flights ({self.status_code})"] if self.query.response.json is None: return ["Flights response did not include valid JSON"] @@ -1155,7 +1155,7 @@ def flights_url(self) -> str: return self.query.request.url.split("?")[0] @property - def flights(self) -> List[Flight]: + def flights(self) -> list[Flight]: if not self.success: return [] if self.rid_version == RIDVersion.f3411_19: @@ -1174,7 +1174,7 @@ def uss_flights( include_recent_positions: bool, rid_version: RIDVersion, session: UTMClientSession, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedUSSFlights: if rid_version == RIDVersion.f3411_19: query = fetch.query_and_describe( @@ -1182,12 +1182,7 @@ def uss_flights( "GET", flights_url, params={ - "view": "{},{},{},{}".format( - area.lat_lo().degrees, - area.lng_lo().degrees, - area.lat_hi().degrees, - area.lng_hi().degrees, - ), + "view": f"{area.lat_lo().degrees},{area.lng_lo().degrees},{area.lat_hi().degrees},{area.lng_hi().degrees}", "include_recent_positions": ( "true" if include_recent_positions else "false" ), @@ -1199,12 +1194,7 @@ def uss_flights( return FetchedUSSFlights(v19_query=query) elif rid_version == RIDVersion.f3411_22a: params = { - "view": "{},{},{},{}".format( - area.lat_lo().degrees, - area.lng_lo().degrees, - area.lat_hi().degrees, - area.lng_hi().degrees, - ), + "view": f"{area.lat_lo().degrees},{area.lng_lo().degrees},{area.lat_hi().degrees},{area.lng_hi().degrees}", } if include_recent_positions: params["recent_positions_duration"] = "60" @@ -1246,9 +1236,9 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code != 200: - return ["Failed to get flight details ({})".format(self.status_code)] + return [f"Failed to get flight details ({self.status_code})"] if self.query.response.json is None: return ["Flight details response did not include valid JSON"] @@ -1282,7 +1272,7 @@ def flights_url(self) -> str: return "/".join(self.query.request.url.split("/")[0:-2]) @property - def details(self) -> Optional[FlightDetails]: + def details(self) -> FlightDetails | None: if not self.success: return None if self.rid_version == RIDVersion.f3411_19: @@ -1301,7 +1291,7 @@ def flight_details( enhanced_details: bool, rid_version: RIDVersion, session: UTMClientSession, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedUSSFlightDetails: url = f"{flights_url}/{flight_id}/details" if rid_version == RIDVersion.f3411_19: @@ -1339,11 +1329,11 @@ def flight_details( class FetchedFlights(ImplicitDict): dss_isa_query: FetchedISAs - uss_flight_queries: Dict[str, FetchedUSSFlights] - uss_flight_details_queries: Dict[str, FetchedUSSFlightDetails] + uss_flight_queries: dict[str, FetchedUSSFlights] + uss_flight_details_queries: dict[str, FetchedUSSFlightDetails] @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if not self.dss_isa_query.success: return self.dss_isa_query.errors result = [] @@ -1354,14 +1344,14 @@ def errors(self) -> List[str]: return result @property - def queries(self) -> List[Query]: + def queries(self) -> list[Query]: result = [self.dss_isa_query.query] result.extend(q.query for q in self.uss_flight_queries.values()) result.extend(q.query for q in self.uss_flight_details_queries.values()) return result @property - def flights(self) -> List[Flight]: + def flights(self) -> list[Flight]: all_flights = [] for q in self.uss_flight_queries.values(): all_flights.extend(q.flights) @@ -1380,7 +1370,7 @@ def all_flights( session: UTMClientSession, dss_base_url: str = "", enhanced_details: bool = False, - dss_participant_id: Optional[str] = None, + dss_participant_id: str | None = None, ) -> FetchedFlights: t = datetime.datetime.now(datetime.UTC) isa_list = isas( @@ -1393,8 +1383,8 @@ def all_flights( participant_id=dss_participant_id, ) - uss_flight_queries: Dict[str, FetchedUSSFlights] = {} - uss_flight_details_queries: Dict[str, FetchedUSSFlightDetails] = {} + uss_flight_queries: dict[str, FetchedUSSFlights] = {} + uss_flight_details_queries: dict[str, FetchedUSSFlightDetails] = {} for flights_url in isa_list.flights_urls: flights_for_url = uss_flights( flights_url, @@ -1449,11 +1439,11 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code == 404: return ["Subscription not present in DSS"] if self.status_code != 200: - return ["Failed to get Subscription ({})".format(self.status_code)] + return [f"Failed to get Subscription ({self.status_code})"] if self.query.response.json is None: return ["Subscription response did not include valid JSON"] @@ -1476,7 +1466,7 @@ def errors(self) -> List[str]: return [] @property - def subscription(self) -> Optional[Subscription]: + def subscription(self) -> Subscription | None: if not self.success: return None if self.rid_version == RIDVersion.f3411_19: @@ -1494,7 +1484,7 @@ def subscription( rid_version: RIDVersion, session: UTMClientSession, dss_base_url: str = "", - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedSubscription: if rid_version == RIDVersion.f3411_19: op = v19.api.OPERATIONS[v19.api.OperationID.GetSubscription] @@ -1550,7 +1540,7 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: # Overall errors if self.status_code != 200: return [f"Failed to search subscriptions in DSS ({self.status_code})"] @@ -1579,7 +1569,7 @@ def errors(self) -> List[str]: return [] @property - def subscriptions(self) -> Dict[str, Subscription]: + def subscriptions(self) -> dict[str, Subscription]: if not self.success: return {} if self.rid_version == RIDVersion.f3411_19: @@ -1599,11 +1589,11 @@ def subscriptions(self) -> Dict[str, Subscription]: def subscriptions( - area: List[s2sphere.LatLng], + area: list[s2sphere.LatLng], rid_version: RIDVersion, session: UTMClientSession, dss_base_url: str = "", - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedSubscriptions: if rid_version == RIDVersion.f3411_19: op = v19.api.OPERATIONS[v19.api.OperationID.SearchSubscriptions] diff --git a/monitoring/monitorlib/fetch/scd.py b/monitoring/monitorlib/fetch/scd.py index d2207d7cad..727e303cbd 100644 --- a/monitoring/monitorlib/fetch/scd.py +++ b/monitoring/monitorlib/fetch/scd.py @@ -1,5 +1,4 @@ import datetime -from typing import Dict, List, Optional import s2sphere import yaml @@ -22,28 +21,22 @@ class FetchedEntityReferences(fetch.Query): """Wrapper to interpret a DSS Entity query as a set of Entity references.""" - entity_type: Optional[str] = None + entity_type: str | None = None @property def success(self) -> bool: return self.error is None @property - def error(self) -> Optional[str]: + def error(self) -> str | None: # Handle any errors if self.status_code != 200: - return "Failed to search {} in DSS ({})".format( - self.entity_type, self.status_code - ) + return f"Failed to search {self.entity_type} in DSS ({self.status_code})" if self.json_result is None: - return "DSS response to search {} was not valid JSON".format( - self.entity_type - ) + return f"DSS response to search {self.entity_type} was not valid JSON" for entity_ref in self.json_result.get(self.entity_type, []): if "id" not in entity_ref: - return "DSS response to search {} included entry without id".format( - self.entity_type - ) + return f"DSS response to search {self.entity_type} included entry without id" if "manager" not in entity_ref: return "DSS response to search {} included {} without manager".format( self.entity_type, entity_ref["id"] @@ -57,7 +50,7 @@ def error(self) -> Optional[str]: return None @property - def references_by_id(self) -> Dict: + def references_by_id(self) -> dict: if self.json_result is None: return {} return {e["id"]: e for e in self.json_result.get(self.entity_type, [])} @@ -101,7 +94,7 @@ def _entity_references( polygon=Polygon.from_latlng_rect(latlngrect=area), ).to_f3548v21() } - url = "/dss/v1/{}/query".format(dss_resource_name) + url = f"/dss/v1/{dss_resource_name}/query" scope = scd.SCOPE_CP if "constraint" in dss_resource_name else scd.SCOPE_SC entity_references = FetchedEntityReferences( fetch.query_and_describe( @@ -132,38 +125,38 @@ def operational_intent_references( class FetchedEntity(fetch.Query): - id_requested: Optional[str] = None - entity_type: Optional[str] = None + id_requested: str | None = None + entity_type: str | None = None @property def success(self) -> bool: return self.error is None @property - def reference(self) -> Optional[Dict]: + def reference(self) -> dict | None: if self.json_result is None: return None return self.json_result.get(self.entity_type, {}).get("reference", None) @property - def details(self) -> Optional[Dict]: + def details(self) -> dict | None: if self.json_result is None: return None return self.json_result.get(self.entity_type, {}).get("details", None) @property - def error(self) -> Optional[str]: - prefix = "USS query for {} {} ".format(self.entity_type, self.id_requested) + def error(self) -> str | None: + prefix = f"USS query for {self.entity_type} {self.id_requested} " if self.status_code != 200: - msg = prefix + "indicated failure ({})".format(self.status_code) + msg = prefix + f"indicated failure ({self.status_code})" if "failure" in self.response: msg += ": " + self.response["failure"] return msg if self.json_result is None: return prefix + "did not return valid JSON" if self.entity_type not in self.json_result: - return prefix + "did not contain {} field".format(self.entity_type) + return prefix + f"did not contain {self.entity_type} field" if self.reference is None: return prefix + "did not contain reference field" if self.details is None: @@ -192,9 +185,7 @@ def _full_entity( entity_id: str, utm_client: infrastructure.UTMClientSession, ) -> FetchedEntity: - uss_entity_url = uss_base_url + "/uss/v1/{}s/{}".format( - uss_resource_name, entity_id - ) + uss_entity_url = uss_base_url + f"/uss/v1/{uss_resource_name}s/{entity_id}" # Query the USS for Entity details scope = scd.SCOPE_CP if "constraint" in uss_resource_name else scd.SCOPE_SC @@ -214,33 +205,33 @@ def operational_intent( class FetchedEntities(ImplicitDict): dss_query: FetchedEntityReferences - uss_queries: Dict[str, FetchedEntity] - cached_uss_queries: Dict[str, FetchedEntity] + uss_queries: dict[str, FetchedEntity] + cached_uss_queries: dict[str, FetchedEntity] @property def success(self) -> bool: return not self.error @property - def error(self) -> Optional[str]: + def error(self) -> str | None: dss_error = self.dss_query.error if dss_error is not None: return dss_error return None @property - def entities_by_id(self) -> Dict[str, FetchedEntity]: + def entities_by_id(self) -> dict[str, FetchedEntity]: entities = self.cached_entities_by_id.copy() for k, v in self.new_entities_by_id.items(): entities[k] = v return entities @property - def new_entities_by_id(self) -> Dict[str, FetchedEntity]: + def new_entities_by_id(self) -> dict[str, FetchedEntity]: return self.uss_queries @property - def cached_entities_by_id(self) -> Dict[str, FetchedEntity]: + def cached_entities_by_id(self) -> dict[str, FetchedEntity]: return self.cached_uss_queries def has_different_content_than(self, other): @@ -275,7 +266,7 @@ def uss_success(self) -> bool: return self.fetched_entity.success @property - def reference(self) -> Dict: + def reference(self) -> dict: return self.reference @property @@ -292,14 +283,14 @@ def _entities( end_time: datetime.datetime, alt_min_m: float = 0, alt_max_m: float = 3048, - entity_cache: Optional[Dict[str, CachedEntity]] = None, + entity_cache: dict[str, CachedEntity] | None = None, ) -> FetchedEntities: fetched_references = _entity_references( dss_resource_name, utm_client, area, start_time, end_time, alt_min_m, alt_max_m ) - uss_queries: Dict[str, FetchedEntity] = {} - cached_queries: Dict[str, FetchedEntity] = {} + uss_queries: dict[str, FetchedEntity] = {} + cached_queries: dict[str, FetchedEntity] = {} if fetched_references.success: if entity_cache is None: entity_cache = {} @@ -336,7 +327,7 @@ def operations( end_time: datetime.datetime, alt_min_m: float = 0, alt_max_m: float = 3048, - operation_cache: Optional[Dict[str, FetchedEntity]] = None, + operation_cache: dict[str, FetchedEntity] | None = None, ) -> FetchedEntities: return _entities( "operational_intent_references", @@ -358,7 +349,7 @@ def constraints( end_time: datetime.datetime, alt_min_m: float = 0, alt_max_m: float = 3048, - constraint_cache: Optional[Dict[str, FetchedEntity]] = None, + constraint_cache: dict[str, FetchedEntity] | None = None, ) -> FetchedEntities: return _entities( "constraint_references", @@ -388,11 +379,11 @@ def was_not_found(self) -> bool: return self.status_code == 404 @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code == 404: return ["Subscription not found"] if self.status_code != 200: - return ["Request to get Subscription failed ({})".format(self.status_code)] + return [f"Request to get Subscription failed ({self.status_code})"] if self.json_result is None: return ["Request to get Subscription did not return valid JSON"] if self.subscription is None: @@ -400,7 +391,7 @@ def errors(self) -> List[str]: return [] @property - def subscription(self) -> Optional[Subscription]: + def subscription(self) -> Subscription | None: try: # We get a ValueError if .parse is fed a None, # or if the JSON can't be parsed as a Subscription. @@ -420,11 +411,11 @@ def success(self) -> bool: return not self.errors @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code == 404: return [] if self.status_code != 200: - return ["Request to get Subscriptions failed ({})".format(self.status_code)] + return [f"Request to get Subscriptions failed ({self.status_code})"] if self.json_result is None: return ["Request to get Subscriptions did not return valid JSON"] try: @@ -435,14 +426,14 @@ def errors(self) -> List[str]: return [] @property - def _subscriptions(self) -> List[Subscription]: + def _subscriptions(self) -> list[Subscription]: return [ ImplicitDict.parse(sub, Subscription) for sub in self.json_result.get("subscriptions", []) ] @property - def subscriptions(self) -> Dict[str, Subscription]: + def subscriptions(self) -> dict[str, Subscription]: if not self.success or self.status_code == 404: return {} else: @@ -455,7 +446,7 @@ def subscriptions(self) -> Dict[str, Subscription]: def get_subscription( utm_client: infrastructure.UTMClientSession, subscription_id: str, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedSubscription: op = OPERATIONS[OperationID.GetSubscription] return FetchedSubscription( @@ -473,7 +464,7 @@ def get_subscription( def query_subscriptions( utm_client: infrastructure.UTMClientSession, volume: SCDVolume4D, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> FetchedSubscriptions: op = OPERATIONS[OperationID.QuerySubscriptions] return FetchedSubscriptions( diff --git a/monitoring/monitorlib/fetch/summarize.py b/monitoring/monitorlib/fetch/summarize.py index cbddc60aed..6123aadf19 100644 --- a/monitoring/monitorlib/fetch/summarize.py +++ b/monitoring/monitorlib/fetch/summarize.py @@ -1,5 +1,4 @@ import copy -from typing import Dict, Optional from . import rid, scd @@ -14,14 +13,14 @@ def limit_long_arrays(obj, limit: int): return obj elif isinstance(obj, list): if len(obj) > limit: - return "".format(len(obj)) + return f"" else: return [limit_long_arrays(item, limit) for item in obj] else: return obj -def isas(fetched: rid.FetchedISAs) -> Dict: +def isas(fetched: rid.FetchedISAs) -> dict: summary = {} if fetched.success: for isa_id, isa in fetched.isas.items(): @@ -32,14 +31,14 @@ def isas(fetched: rid.FetchedISAs) -> Dict: del isa_summary["id"] if "owner" in isa_summary: del isa_summary["owner"] - isa_key = "{} ({})".format(isa.id, isa.owner) + isa_key = f"{isa.id} ({isa.owner})" summary[isa.flights_url][isa_key] = isa_summary else: summary["error"] = fetched.errors return summary -def _entity(fetched: scd.FetchedEntities, id: str) -> Dict: +def _entity(fetched: scd.FetchedEntities, id: str) -> dict: entity = fetched.entities_by_id[id] if entity.success: return { @@ -55,7 +54,7 @@ def _entity(fetched: scd.FetchedEntities, id: str) -> Dict: } -def entities(fetched: scd.FetchedEntities, entity_type: Optional[str] = None) -> Dict: +def entities(fetched: scd.FetchedEntities, entity_type: str | None = None) -> dict: if fetched.success: if entity_type is not None: return { @@ -76,7 +75,7 @@ def entities(fetched: scd.FetchedEntities, entity_type: Optional[str] = None) -> } -def flights(fetched: rid.FetchedFlights) -> Dict: +def flights(fetched: rid.FetchedFlights) -> dict: if fetched.success: isas_by_url = {} owners_by_url = {} @@ -101,7 +100,7 @@ def flights(fetched: rid.FetchedFlights) -> Dict: flight["details"] = fetched.uss_flight_details_queries[ rid_flight.id ].details - summary["{} ({})".format(rid_flight.id, owner)] = flight + summary[f"{rid_flight.id} ({owner})"] = flight return summary else: return {"errors": fetched.errors} diff --git a/monitoring/monitorlib/formatting.py b/monitoring/monitorlib/formatting.py index ca6663b661..3ab1d1ad7e 100644 --- a/monitoring/monitorlib/formatting.py +++ b/monitoring/monitorlib/formatting.py @@ -1,6 +1,5 @@ import datetime import enum -from typing import Dict, List, Tuple import arrow from termcolor import colored @@ -43,7 +42,7 @@ def _update_overall(overall: Change, field: Change): raise ValueError("Unexpected change configuration") -def dict_changes(a: Dict, b: Dict) -> Tuple[Dict, Dict, Change]: +def dict_changes(a: dict, b: dict) -> tuple[dict, dict, Change]: values = {} changes = {} overall = Change.NOCHANGE @@ -86,7 +85,7 @@ def dict_changes(a: Dict, b: Dict) -> Tuple[Dict, Dict, Change]: return values, changes, overall -def diff_lines(values: Dict, changes: Dict) -> List[str]: +def diff_lines(values: dict, changes: dict) -> list[str]: lines = [] for k, v in values.items(): c = changes[k] @@ -98,11 +97,11 @@ def diff_lines(values: Dict, changes: Dict) -> List[str]: lines.extend(" " + line for line in diff_lines(v, c)) else: if c == Change.ADDED: - lines.append(colored("{}: {}".format(k, v), "green")) + lines.append(colored(f"{k}: {v}", "green")) elif c == Change.CHANGED: lines.append(k + ": " + colored(str(v), "yellow")) elif c == Change.REMOVED: - lines.append(colored("{}: {}".format(k, v), "red")) + lines.append(colored(f"{k}: {v}", "red")) return lines @@ -140,7 +139,7 @@ def make_datetime(t) -> datetime.datetime: elif isinstance(t, datetime.datetime): return arrow.get(t).datetime else: - raise ValueError("Could not convert {} to datetime".format(str(type(t)))) + raise ValueError(f"Could not convert {str(type(t))} to datetime") def limit_resolution(value: float, resolution: float) -> float: diff --git a/monitoring/monitorlib/geo.py b/monitoring/monitorlib/geo.py index 786259155b..3e55980eb8 100644 --- a/monitoring/monitorlib/geo.py +++ b/monitoring/monitorlib/geo.py @@ -3,7 +3,6 @@ import math import os from enum import Enum -from typing import List, Optional, Tuple, Union import numpy as np import s2sphere @@ -65,11 +64,9 @@ class LatLngPoint(ImplicitDict): @staticmethod def from_f3411( - position: Union[ - f3411v19.RIDAircraftPosition, - f3411v22a.RIDAircraftPosition, - f3411testing_injection.RIDAircraftPosition, - ], + position: f3411v19.RIDAircraftPosition + | f3411v22a.RIDAircraftPosition + | f3411testing_injection.RIDAircraftPosition, ): return LatLngPoint( lat=position.lat, @@ -112,7 +109,7 @@ def in_meters(self) -> float: class Polygon(ImplicitDict): - vertices: Optional[List[LatLngPoint]] + vertices: list[LatLngPoint] | None def vertex_average(self) -> LatLngPoint: lat = sum(p.lat for p in self.vertices) / len(self.vertices) @@ -120,13 +117,13 @@ def vertex_average(self) -> LatLngPoint: return LatLngPoint(lat=lat, lng=lng) @staticmethod - def from_coords(coords: List[Tuple[float, float]]) -> Polygon: + def from_coords(coords: list[tuple[float, float]]) -> Polygon: return Polygon( vertices=[LatLngPoint(lat=lat, lng=lng) for (lat, lng) in coords] ) @staticmethod - def from_latlng_coords(coords: List[LatLng]) -> Polygon: + def from_latlng_coords(coords: list[LatLng]) -> Polygon: return Polygon( vertices=[ LatLngPoint(lat=p.lat().degrees, lng=p.lng().degrees) for p in coords @@ -153,7 +150,7 @@ def from_latlng_rect(latlngrect: s2sphere.LatLngRect) -> Polygon: ) @staticmethod - def from_f3548v21(vol: Union[f3548v21.Polygon, dict]) -> Polygon: + def from_f3548v21(vol: f3548v21.Polygon | dict) -> Polygon: if not isinstance(vol, f3548v21.Polygon) and isinstance(vol, dict): vol = ImplicitDict.parse(vol, f3548v21.Polygon) return Polygon( @@ -175,7 +172,7 @@ def from_meters( ) @staticmethod - def from_f3548v21(vol: Union[f3548v21.Circle, dict]) -> Circle: + def from_f3548v21(vol: f3548v21.Circle | dict) -> Circle: if not isinstance(vol, f3548v21.Circle) and isinstance(vol, dict): vol = ImplicitDict.parse(vol, f3548v21.Circle) return Circle( @@ -198,7 +195,7 @@ class Altitude(ImplicitDict): units: DistanceUnits @staticmethod - def w84m(value: Optional[float]) -> Optional[Altitude]: + def w84m(value: float | None) -> Altitude | None: if value is None: return None return Altitude(value=value, reference=AltitudeDatum.W84, units=DistanceUnits.M) @@ -211,17 +208,17 @@ def to_flight_planning_api(self) -> fp_api.Altitude: ) @staticmethod - def from_f3548v21(vol: Union[f3548v21.Altitude, dict]) -> Altitude: + def from_f3548v21(vol: f3548v21.Altitude | dict) -> Altitude: return ImplicitDict.parse(vol, Altitude) class Volume3D(ImplicitDict): - outline_circle: Optional[Circle] = None - outline_polygon: Optional[Polygon] = None - altitude_lower: Optional[Altitude] = None - altitude_upper: Optional[Altitude] = None + outline_circle: Circle | None = None + outline_polygon: Polygon | None = None + altitude_lower: Altitude | None = None + altitude_upper: Altitude | None = None - def altitude_lower_wgs84_m(self, default_value: Optional[float] = None) -> float: + def altitude_lower_wgs84_m(self, default_value: float | None = None) -> float: if self.altitude_lower is None: if default_value is None: raise ValueError("Lower altitude was not specified") @@ -237,7 +234,7 @@ def altitude_lower_wgs84_m(self, default_value: Optional[float] = None) -> float ) return self.altitude_lower.value - def altitude_upper_wgs84_m(self, default_value: Optional[float] = None) -> float: + def altitude_upper_wgs84_m(self, default_value: float | None = None) -> float: if self.altitude_upper is None: if default_value is None: raise ValueError("Upper altitude was not specified") @@ -264,7 +261,7 @@ def intersects_vol3(self, vol3_2: Volume3D) -> bool: circle = vol3_1.outline_circle if circle.radius.units != "M": raise NotImplementedError( - "Unsupported circle radius units: {}".format(circle.radius.units) + f"Unsupported circle radius units: {circle.radius.units}" ) ref = s2sphere.LatLng.from_degrees(circle.center.lat, circle.center.lng) footprint1 = shapely.geometry.Point(0, 0).buffer( @@ -284,7 +281,7 @@ def intersects_vol3(self, vol3_2: Volume3D) -> bool: circle = vol3_2.outline_circle if circle.radius.units != "M": raise NotImplementedError( - "Unsupported circle radius units: {}".format(circle.radius.units) + f"Unsupported circle radius units: {circle.radius.units}" ) xy = flatten( ref, s2sphere.LatLng.from_degrees(circle.center.lat, circle.center.lng) @@ -408,7 +405,7 @@ def to_geospatial_map_api(self) -> geospatial_map_api.Volume3D: ) @staticmethod - def from_f3548v21(vol: Union[f3548v21.Volume3D, dict]) -> Volume3D: + def from_f3548v21(vol: f3548v21.Volume3D | dict) -> Volume3D: if not isinstance(vol, f3548v21.Volume3D) and isinstance(vol, dict): vol = ImplicitDict.parse(vol, f3548v21.Volume3D) kwargs = {} @@ -442,7 +439,7 @@ def make_latlng_rect(area) -> s2sphere.LatLngRect: coords = area.split(",") if len(coords) != 4: raise ValueError( - "Expected lat,lng,lat,lng; found %d coordinates instead" % len(coords) + f"Expected lat,lng,lat,lng; found {len(coords)} coordinates instead" ) lat1 = validate_lat(coords[0]) lng1 = validate_lng(coords[1]) @@ -483,12 +480,7 @@ def make_latlng_rect(area) -> s2sphere.LatLngRect: def rect_str(rect: s2sphere.LatLngRect) -> str: - return "({}, {})-({}, {})".format( - rect.lo().lat().degrees, - rect.lo().lng().degrees, - rect.hi().lat().degrees, - rect.hi().lng().degrees, - ) + return f"({rect.lo().lat().degrees}, {rect.lo().lng().degrees})-({rect.hi().lat().degrees}, {rect.hi().lng().degrees})" def shift_rect_lng(rect: s2sphere.LatLngRect, shift: float) -> s2sphere.LatLngRect: @@ -503,21 +495,21 @@ def shift_rect_lng(rect: s2sphere.LatLngRect, shift: float) -> s2sphere.LatLngRe ) -def validate_lat(lat: Union[str, float]) -> float: +def validate_lat(lat: str | float) -> float: lat = float(lat) if lat < -90 or lat > 90: raise ValueError("Latitude must be in [-90, 90] range") return lat -def validate_lng(lng: Union[str, float]) -> float: +def validate_lng(lng: str | float) -> float: lng = float(lng) if lng < -180 or lng > 180: raise ValueError("Longitude must be in [-180, 180] range") return lng -def flatten(reference: s2sphere.LatLng, point: s2sphere.LatLng) -> Tuple[float, float]: +def flatten(reference: s2sphere.LatLng, point: s2sphere.LatLng) -> tuple[float, float]: """Locally flatten a lat-lng point to (dx, dy) in meters from reference.""" return ( (point.lng().degrees - reference.lng().degrees) @@ -533,7 +525,7 @@ def flatten(reference: s2sphere.LatLng, point: s2sphere.LatLng) -> Tuple[float, def unflatten( - reference: s2sphere.LatLng, point: Tuple[float, float] + reference: s2sphere.LatLng, point: tuple[float, float] ) -> s2sphere.LatLng: """Locally unflatten a (dx, dy) point to an absolute lat-lng point.""" return s2sphere.LatLng.from_degrees( @@ -550,7 +542,7 @@ def area_of_latlngrect(rect: s2sphere.LatLngRect) -> float: return EARTH_AREA_M2 * rect.area() / (4 * math.pi) -def bounding_rect(latlngs: List[Tuple[float, float]]) -> s2sphere.LatLngRect: +def bounding_rect(latlngs: list[tuple[float, float]]) -> s2sphere.LatLngRect: lat_min = 90 lat_max = -90 lng_min = 360 @@ -571,7 +563,7 @@ def get_latlngrect_diagonal_km(rect: s2sphere.LatLngRect) -> float: return rect.lo().get_distance(rect.hi()).degrees * EARTH_CIRCUMFERENCE_KM / 360 -def get_latlngrect_vertices(rect: s2sphere.LatLngRect) -> List[s2sphere.LatLng]: +def get_latlngrect_vertices(rect: s2sphere.LatLngRect) -> list[s2sphere.LatLng]: """Returns the rect as a list of vertices""" return [ s2sphere.LatLng.from_angles(lat=rect.lat_lo(), lng=rect.lng_lo()), @@ -622,7 +614,7 @@ def expand( lng_max=self.lng_max + east_meters * 360 / longitude_length, ) - def to_vertices(self) -> List[s2sphere.LatLng]: + def to_vertices(self) -> list[s2sphere.LatLng]: return [ s2sphere.LatLng.from_degrees(self.lat_min, self.lng_min), s2sphere.LatLng.from_degrees(self.lat_max, self.lng_min), @@ -644,7 +636,7 @@ def latitude_degrees(distance_meters: float) -> float: return 360 * distance_meters / EARTH_CIRCUMFERENCE_M -_egm96: Optional[Spline] = None +_egm96: Spline | None = None """Cached EGM96 geoid interpolation function with inverted latitude""" @@ -680,7 +672,7 @@ def egm96_geoid_offset(p: s2sphere.LatLng) -> float: return _egm96.ev(-lat, lng) -def center_of_mass(in_points: List[LatLng]) -> LatLng: +def center_of_mass(in_points: list[LatLng]) -> LatLng: """Compute the center of mass of a polygon defined by a list of points.""" if len(in_points) == 0: raise ValueError("Cannot compute center of mass of empty polygon") @@ -691,7 +683,7 @@ def center_of_mass(in_points: List[LatLng]) -> LatLng: ) -def generate_slight_overlap_area(in_points: List[LatLng]) -> List[LatLng]: +def generate_slight_overlap_area(in_points: list[LatLng]) -> list[LatLng]: """ Takes a list of LatLng points and returns a list of LatLng points that represents a polygon only slightly overlapping with the input, and that is roughly half the diameter of the input. @@ -724,8 +716,8 @@ def generate_slight_overlap_area(in_points: List[LatLng]) -> List[LatLng]: def generate_area_in_vicinity( - in_points: List[LatLng], relative_distance: float -) -> List[LatLng]: + in_points: list[LatLng], relative_distance: float +) -> list[LatLng]: """ Takes a list of LatLng points and returns a list of LatLng points that represents a non-contiguous area in the vicinity of the input. diff --git a/monitoring/monitorlib/geo_test.py b/monitoring/monitorlib/geo_test.py index 647d3a9473..a8396638dd 100644 --- a/monitoring/monitorlib/geo_test.py +++ b/monitoring/monitorlib/geo_test.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from s2sphere import LatLng from monitoring.monitorlib.geo import ( @@ -10,7 +8,7 @@ MAX_DIFFERENCE = 0.001 -def _points(in_points: List[Tuple[float, float]]) -> List[LatLng]: +def _points(in_points: list[tuple[float, float]]) -> list[LatLng]: return [LatLng.from_degrees(*p) for p in in_points] @@ -41,7 +39,7 @@ def test_generate_slight_overlap_area(): ) == _points([(1, -1), (1, -1.5), (1.5, -1.5), (1.5, -1)]) -def _approx_equals(p1: List[LatLng], p2: List[LatLng]) -> bool: +def _approx_equals(p1: list[LatLng], p2: list[LatLng]) -> bool: return all([p1[i].approx_equals(p2[i], MAX_DIFFERENCE) for i in range(len(p1))]) diff --git a/monitoring/monitorlib/geotemporal.py b/monitoring/monitorlib/geotemporal.py index 2c924ce5f7..37f818e042 100644 --- a/monitoring/monitorlib/geotemporal.py +++ b/monitoring/monitorlib/geotemporal.py @@ -2,7 +2,6 @@ import math from datetime import datetime, timedelta -from typing import Dict, List, Optional, Tuple, Union import s2sphere as s2sphere from implicitdict import ImplicitDict, StringBasedTimeDelta @@ -20,31 +19,31 @@ class Volume4DTemplate(ImplicitDict): - outline_polygon: Optional[Polygon] = None + outline_polygon: Polygon | None = None """Polygonal 2D outline/footprint of the specified area. May not be defined if outline_circle is defined.""" - outline_circle: Optional[Circle] = None + outline_circle: Circle | None = None """Circular outline/footprint of the specified area. May not be defined if outline_polygon is defined.""" - start_time: Optional[TestTime] = None + start_time: TestTime | None = None """The time at which the virtual user may start using the specified geospatial area for their flight. May not be defined if duration and end_time are defined.""" - end_time: Optional[TestTime] = None + end_time: TestTime | None = None """The time at which the virtual user will be finished using the specified geospatial area for their flight. May not be defined if duration and start_time are defined.""" - duration: Optional[StringBasedTimeDelta] = None + duration: StringBasedTimeDelta | None = None """If only one of start_time and end_time is specified, then the other time should be separated from the specified time by this amount. May not be defined in both start_time and end_time are defined.""" - altitude_lower: Optional[Altitude] = None + altitude_lower: Altitude | None = None """The minimum altitude at which the virtual user will fly while using this volume for their flight.""" - altitude_upper: Optional[Altitude] = None + altitude_upper: Altitude | None = None """The maximum altitude at which the virtual user will fly while using this volume for their flight.""" - transformations: Optional[List[Transformation]] = None + transformations: list[Transformation] | None = None """If specified, transform this volume according to these transformations in order.""" - def resolve(self, times: Dict[TimeDuringTest, Time]) -> Volume4D: + def resolve(self, times: dict[TimeDuringTest, Time]) -> Volume4D: """Resolve Volume4DTemplate into concrete Volume4D.""" # Make 3D volume kwargs = {} @@ -103,8 +102,8 @@ class Volume4D(ImplicitDict): """Generic representation of a 4D volume, usable across multiple standards and formats.""" volume: Volume3D - time_start: Optional[Time] = None - time_end: Optional[Time] = None + time_start: Time | None = None + time_end: Time | None = None def offset_time(self, dt: timedelta) -> Volume4D: kwargs = {"volume": self.volume} @@ -143,7 +142,7 @@ def rect_bounds(self) -> s2sphere.LatLngRect: circle = self.volume.outline_circle if circle.radius.units != "M": raise NotImplementedError( - "Unsupported circle radius units: {}".format(circle.radius.units) + f"Unsupported circle radius units: {circle.radius.units}" ) lat_radius = 360 * circle.radius.value / geo.EARTH_CIRCUMFERENCE_M lng_radius = ( @@ -161,12 +160,12 @@ def rect_bounds(self) -> s2sphere.LatLngRect: @staticmethod def from_values( - t0: Optional[datetime] = None, - t1: Optional[datetime] = None, - alt0: Optional[float] = None, - alt1: Optional[float] = None, - circle: Optional[Circle] = None, - polygon: Optional[Polygon] = None, + t0: datetime | None = None, + t1: datetime | None = None, + alt0: float | None = None, + alt1: float | None = None, + circle: Circle | None = None, + polygon: Polygon | None = None, ) -> Volume4D: kwargs = dict() if circle is not None: @@ -246,7 +245,7 @@ def to_geospatial_map_api(self) -> geospatial_map_api.Volume4D: return geospatial_map_api.Volume4D(**kwargs) -class Volume4DCollection(List[Volume4D]): +class Volume4DCollection(list[Volume4D]): def __add__(self, other): if isinstance(other, Volume4D): full_list = [] @@ -274,7 +273,7 @@ def __iadd__(self, other): ) @property - def time_start(self) -> Optional[Time]: + def time_start(self) -> Time | None: return ( Time(min(v.time_start.datetime for v in self)) if all("time_start" in v and v.time_start for v in self) @@ -282,7 +281,7 @@ def time_start(self) -> Optional[Time]: ) @property - def time_end(self) -> Optional[Time]: + def time_end(self) -> Time | None: return ( Time(max(v.time_end.datetime for v in self)) if all("time_end" in v and v.time_end for v in self) @@ -313,9 +312,7 @@ def rect_bounds(self) -> s2sphere.LatLngRect: circle = vol4.volume.outline_circle if circle.radius.units != "M": raise NotImplementedError( - "Unsupported circle radius units: {}".format( - circle.radius.units - ) + f"Unsupported circle radius units: {circle.radius.units}" ) lat_radius = 360 * circle.radius.value / geo.EARTH_CIRCUMFERENCE_M lng_radius = ( @@ -360,7 +357,7 @@ def bounding_volume(self) -> Volume4D: return Volume4D(**kwargs) @property - def meter_altitude_bounds(self) -> Tuple[float, float]: + def meter_altitude_bounds(self) -> tuple[float, float]: alt_lo = min( vol4.volume.altitude_lower.value for vol4 in self @@ -399,21 +396,21 @@ def intersects_vol4s(self, vol4s_2: Volume4DCollection) -> bool: return False @staticmethod - def from_f3548v21(vol4s: List[f3548v21.Volume4D]) -> Volume4DCollection: + def from_f3548v21(vol4s: list[f3548v21.Volume4D]) -> Volume4DCollection: volumes = [Volume4D.from_f3548v21(v) for v in vol4s] return Volume4DCollection(volumes) @staticmethod def from_interuss_scd_api( - vol4s: List[interuss_scd_api.Volume4D], + vol4s: list[interuss_scd_api.Volume4D], ) -> Volume4DCollection: volumes = [Volume4D.from_interuss_scd_api(v) for v in vol4s] return Volume4DCollection(volumes) - def to_f3548v21(self) -> List[f3548v21.Volume4D]: + def to_f3548v21(self) -> list[f3548v21.Volume4D]: return [v.to_f3548v21() for v in self] - def to_interuss_scd_api(self) -> List[interuss_scd_api.Volume4D]: + def to_interuss_scd_api(self) -> list[interuss_scd_api.Volume4D]: return [v.to_interuss_scd_api() for v in self] def has_active_volume(self, time_ref: datetime) -> bool: @@ -422,18 +419,16 @@ def has_active_volume(self, time_ref: datetime) -> bool: ) -class Volume4DTemplateCollection(List[Volume4DTemplate]): +class Volume4DTemplateCollection(list[Volume4DTemplate]): pass def end_time_of( - volume_or_volumes: Union[ - f3548v21.Volume4D, - Volume4D, - List[Union[f3548v21.Volume4D, Volume4D]], - Volume4DCollection, - ], -) -> Optional[Time]: + volume_or_volumes: f3548v21.Volume4D + | Volume4D + | list[f3548v21.Volume4D | Volume4D] + | Volume4DCollection, +) -> Time | None: """Retrieve the end time of a volume or list of volumes.""" if isinstance(volume_or_volumes, f3548v21.Volume4D): if "time_end" in volume_or_volumes and volume_or_volumes.time_end: diff --git a/monitoring/monitorlib/idempotency.py b/monitoring/monitorlib/idempotency.py index b12e5be95e..1edfb152a9 100644 --- a/monitoring/monitorlib/idempotency.py +++ b/monitoring/monitorlib/idempotency.py @@ -1,8 +1,8 @@ import base64 import hashlib import json +from collections.abc import Callable from functools import wraps -from typing import Callable, Dict, Optional import arrow import flask @@ -21,17 +21,17 @@ class Response(ImplicitDict): Note that this object is never actually used (in order to maximize performance); instead it serves as documentation of the structure of the fields within a plain JSON dict/object.""" - json: Optional[dict] - body: Optional[str] + json: dict | None + body: str | None code: int timestamp: StringBasedDateTime -def _get_responses(raw: bytes) -> Dict[str, Response]: +def _get_responses(raw: bytes) -> dict[str, Response]: return json.loads(raw.decode("utf-8")) -def _set_responses(responses: Dict[str, Response]) -> bytes: +def _set_responses(responses: dict[str, Response]) -> bytes: while True: s = json.dumps(responses) if len(s) <= _max_request_buffer_size: @@ -58,7 +58,7 @@ def _set_responses(responses: Dict[str, Response]) -> bytes: ) -def get_hashed_request_id() -> Optional[str]: +def get_hashed_request_id() -> str | None: """Retrieves an identifier for the request by hashing key characteristics of the request.""" characteristics = flask.request.method + flask.request.url if flask.request.json: @@ -70,7 +70,7 @@ def get_hashed_request_id() -> Optional[str]: ).decode("utf-8") -def idempotent_request(get_request_id: Optional[Callable[[], Optional[str]]] = None): +def idempotent_request(get_request_id: Callable[[], str | None] | None = None): """Decorator for idempotent Flask view handlers. When subsequent requests are received with the same request identifier, this decorator will use a recent cached diff --git a/monitoring/monitorlib/ids.py b/monitoring/monitorlib/ids.py index 4952ca5a84..d8f4ca5e26 100644 --- a/monitoring/monitorlib/ids.py +++ b/monitoring/monitorlib/ids.py @@ -19,9 +19,7 @@ def make_id(code: str) -> uuid.UUID: Returns: Pseudorandom test ID in UUIDv4 format. """ - digest = bytearray( - hashlib.sha1("{} {}".format(mac, code).encode("utf-8")).digest()[-16:] - ) + digest = bytearray(hashlib.sha1(f"{mac} {code}".encode()).digest()[-16:]) digest[0] = 0 digest[1] = 0 digest[2] = 0xFF diff --git a/monitoring/monitorlib/infrastructure.py b/monitoring/monitorlib/infrastructure.py index 5ac364abb0..3c5d31672c 100644 --- a/monitoring/monitorlib/infrastructure.py +++ b/monitoring/monitorlib/infrastructure.py @@ -3,7 +3,6 @@ import functools import urllib.parse from enum import Enum -from typing import Dict, List, Optional import jwt import requests @@ -27,18 +26,18 @@ """Specification for means by which to obtain access tokens.""" -class AuthAdapter(object): +class AuthAdapter: """Base class for an adapter that add JWTs to requests.""" def __init__(self): self._tokens = {} - def issue_token(self, intended_audience: str, scopes: List[str]) -> str: + def issue_token(self, intended_audience: str, scopes: list[str]) -> str: """Subclasses must return a bearer token for the given audience.""" 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) -> dict[str, str]: if scopes is None: scopes = ALL_SCOPES scopes = [s.value if isinstance(s, Enum) else s for s in scopes] @@ -57,11 +56,11 @@ def get_headers(self, url: str, scopes: List[str] = None) -> Dict[str, str]: self._tokens[intended_audience][scope_string] = token return {"Authorization": "Bearer " + token} - def add_headers(self, request: requests.PreparedRequest, scopes: List[str]): + 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 - def get_sub(self) -> Optional[str]: + def get_sub(self) -> str | None: """Retrieve `sub` claim from one of the existing tokens""" for _, tokens_by_scope in self._tokens.items(): for token in tokens_by_scope.values(): @@ -85,8 +84,8 @@ class UTMClientSession(requests.Session): def __init__( self, prefix_url: str, - auth_adapter: Optional[AuthAdapter] = None, - timeout_seconds: Optional[float] = None, + auth_adapter: AuthAdapter | None = None, + timeout_seconds: float | None = None, ): super().__init__() @@ -147,8 +146,8 @@ class AsyncUTMTestSession: def __init__( self, prefix_url: str, - auth_adapter: Optional[AuthAdapter] = None, - timeout_seconds: Optional[float] = None, + auth_adapter: AuthAdapter | None = None, + timeout_seconds: float | None = None, ): self._client = None loop = asyncio.get_event_loop() @@ -241,7 +240,7 @@ async def delete(self, url, **kwargs): ) -def default_scopes(scopes: List[str]): +def default_scopes(scopes: list[str]): """Decorator for tests that modifies UTMClientSession args to use scopes. A test function decorated with this decorator will modify all arguments which @@ -308,7 +307,7 @@ def default_scope(scope: str): return default_scopes([scope]) -def get_token_claims(headers: Dict) -> Dict: +def get_token_claims(headers: dict) -> dict: auth_key = [key for key in headers if key.lower() == "authorization"] if len(auth_key) == 0: return {"error": "Missing Authorization header"} diff --git a/monitoring/monitorlib/inspection.py b/monitoring/monitorlib/inspection.py index 7b1edf0b4d..0350cdd196 100644 --- a/monitoring/monitorlib/inspection.py +++ b/monitoring/monitorlib/inspection.py @@ -1,7 +1,6 @@ import importlib import inspect import pkgutil -from typing import Type def import_submodules(module) -> None: @@ -22,15 +21,13 @@ def get_module_object_by_name(parent_module, object_name: str): for component in object_name.split("."): if not hasattr(module_object, component): raise ValueError( - "Could not find component {} defined in {} while trying to locate {}".format( - component, module_object.__name__, object_name - ) + f"Could not find component {component} defined in {module_object.__name__} while trying to locate {object_name}" ) module_object = getattr(module_object, component) return module_object -def fullname(class_type: Type) -> str: +def fullname(class_type: type) -> str: module = class_type.__module__ if module == "builtins": if hasattr(class_type, "__qualname__"): diff --git a/monitoring/monitorlib/kml/f3548v21.py b/monitoring/monitorlib/kml/f3548v21.py index 47f7f33bb1..7add7bb9d9 100644 --- a/monitoring/monitorlib/kml/f3548v21.py +++ b/monitoring/monitorlib/kml/f3548v21.py @@ -1,5 +1,3 @@ -from typing import List - from pykml.factory import KML_ElementMaker as kml from uas_standards.astm.f3548.v21.api import ( OperationalIntent, @@ -70,7 +68,7 @@ def op_intent_refs_query( ) -def f3548v21_styles() -> List[kml.Style]: +def f3548v21_styles() -> list[kml.Style]: """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 edaf6810c4..5f89dff3bf 100644 --- a/monitoring/monitorlib/kml/flight_planning.py +++ b/monitoring/monitorlib/kml/flight_planning.py @@ -1,5 +1,3 @@ -from typing import List - from pykml.factory import KML_ElementMaker as kml from uas_standards.interuss.automated_testing.flight_planning.v1.api import ( UpsertFlightPlanRequest, @@ -39,7 +37,7 @@ def upsert_flight_plan( return folder -def flight_planning_styles() -> List[kml.Style]: +def flight_planning_styles() -> list[kml.Style]: """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 d553bd9d72..9273e6a843 100644 --- a/monitoring/monitorlib/kml/generation.py +++ b/monitoring/monitorlib/kml/generation.py @@ -1,5 +1,4 @@ import math -from typing import List, Optional, Union import s2sphere from pykml.factory import KML_ElementMaker as kml @@ -40,7 +39,7 @@ def _altitude_mode_of(altitude: Altitude) -> str: ) -def _distance_value_of(distance: Union[Altitude, Radius]) -> float: +def _distance_value_of(distance: Altitude | Radius) -> float: if distance.units == DistanceUnits.M: return distance.value elif distance.units == DistanceUnits.FT: @@ -51,9 +50,9 @@ def _distance_value_of(distance: Union[Altitude, Radius]) -> float: def make_placemark_from_volume( v4: Volume4D, - name: Optional[str] = None, - style_url: Optional[str] = None, - description: Optional[str] = None, + 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 @@ -188,7 +187,7 @@ def make_placemark_from_volume( return placemark -def query_styles() -> List[kml.Style]: +def query_styles() -> list[kml.Style]: """Provides KML styles for query areas.""" return [ kml.Style( diff --git a/monitoring/monitorlib/kml/parsing.py b/monitoring/monitorlib/kml/parsing.py index f0c857b69e..84e060779f 100644 --- a/monitoring/monitorlib/kml/parsing.py +++ b/monitoring/monitorlib/kml/parsing.py @@ -48,9 +48,7 @@ def get_folder_details(folder_elem): if placemark_name.startswith("speed:"): if not get_polygon_speed(placemark_name): raise ValueError( - 'Could not determine Polygon speed from Placemark "{}"'.format( - placemark_name - ) + f'Could not determine Polygon speed from Placemark "{placemark_name}"' ) polygon_coords = get_coordinates_from_kml( polygons[0].outerBoundaryIs.LinearRing.coordinates diff --git a/monitoring/monitorlib/multiprocessing.py b/monitoring/monitorlib/multiprocessing.py index 960a2f4861..e581c71569 100644 --- a/monitoring/monitorlib/multiprocessing.py +++ b/monitoring/monitorlib/multiprocessing.py @@ -1,10 +1,11 @@ import json import multiprocessing import multiprocessing.shared_memory -from typing import Any, Callable, Optional +from collections.abc import Callable +from typing import Any -class SynchronizedValue(object): +class SynchronizedValue: """Represents a value synchronized across multiple processes. The shared value can be read with .value or updated in a transaction. A @@ -33,8 +34,8 @@ def __init__( self, initial_value, capacity_bytes: int = 10e6, - encoder: Optional[Callable[[Any], bytes]] = None, - decoder: Optional[Callable[[bytes], Any]] = None, + encoder: Callable[[Any], bytes] | None = None, + decoder: Callable[[bytes], Any] | None = None, ): """Creates a value synchronized across multiple processes. @@ -64,9 +65,7 @@ def _get_value(self): ) if content_len + self.SIZE_BYTES > self._shared_memory.size: raise RuntimeError( - "Shared memory claims to have {} bytes of content when buffer size only allows {}".format( - content_len, self._shared_memory.size - self.SIZE_BYTES - ) + f"Shared memory claims to have {content_len} bytes of content when buffer size only allows {self._shared_memory.size - self.SIZE_BYTES}" ) content = bytes( self._shared_memory.buf[self.SIZE_BYTES : content_len + self.SIZE_BYTES] @@ -78,9 +77,7 @@ def _set_value(self, value): content_len = len(content) if content_len + self.SIZE_BYTES > self._shared_memory.size: raise RuntimeError( - "Tried to write {} bytes into a SynchronizedValue with only {} bytes of capacity".format( - content_len, self._shared_memory.size - self.SIZE_BYTES - ) + f"Tried to write {content_len} bytes into a SynchronizedValue with only {self._shared_memory.size - self.SIZE_BYTES} bytes of capacity" ) self._shared_memory.buf[0 : self.SIZE_BYTES] = content_len.to_bytes( self.SIZE_BYTES, "big" diff --git a/monitoring/monitorlib/mutate/rid.py b/monitoring/monitorlib/mutate/rid.py index 2bbc5caa34..cd23d20979 100644 --- a/monitoring/monitorlib/mutate/rid.py +++ b/monitoring/monitorlib/mutate/rid.py @@ -1,5 +1,4 @@ import datetime -from typing import Dict, List, Optional, Set, Union import s2sphere import uas_standards.astm.f3411.v19.api as v19_api @@ -20,7 +19,7 @@ class ChangedSubscription(RIDQuery): """Version-independent representation of a subscription following a change in the DSS.""" - mutation: Optional[str] = None + mutation: str | None = None @property def _v19_response(self) -> v19_api.PutSubscriptionResponse: @@ -37,9 +36,9 @@ def _v22a_response(self) -> v22a_api.PutSubscriptionResponse: ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code != 200: - return ["Failed to mutate subscription ({})".format(self.status_code)] + return [f"Failed to mutate subscription ({self.status_code})"] if self.query.response.json is None: return ["Subscription response did not include valid JSON"] @@ -64,7 +63,7 @@ def errors(self) -> List[str]: return [] @property - def subscription(self) -> Optional[Subscription]: + def subscription(self) -> Subscription | None: if not self.success: return None if self.rid_version == RIDVersion.f3411_19: @@ -77,7 +76,7 @@ def subscription(self) -> Optional[Subscription]: ) @property - def isas(self) -> List[ISA]: + 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] elif self.rid_version == RIDVersion.f3411_22a: @@ -89,17 +88,17 @@ def isas(self) -> List[ISA]: def upsert_subscription( - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, uss_base_url: str, subscription_id: str, rid_version: RIDVersion, utm_client: infrastructure.UTMClientSession, - subscription_version: Optional[str] = None, - participant_id: Optional[str] = None, + subscription_version: str | None = None, + participant_id: str | None = None, ) -> ChangedSubscription: mutation = "create" if subscription_version is None else "update" if rid_version == RIDVersion.f3411_19: @@ -180,7 +179,7 @@ def delete_subscription( subscription_version: str, rid_version: RIDVersion, utm_client: infrastructure.UTMClientSession, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> ChangedSubscription: if rid_version == RIDVersion.f3411_19: op = v19_api.OPERATIONS[v19_api.OperationID.DeleteSubscription] @@ -220,18 +219,18 @@ class ISAChangeNotification(RIDQuery): """Version-independent representation of response to a USS notification following an ISA change in the DSS.""" @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: # Tolerate not-strictly-correct 200 response if self.status_code != 204 and self.status_code != 200: - return ["Failed to notify ({})".format(self.status_code)] + return [f"Failed to notify ({self.status_code})"] return [] class SubscriberToNotify(ImplicitDict): """Version-independent representation of a subscriber to notify of a change in the DSS.""" - v19_value: Optional[v19_api.SubscriberToNotify] = None - v22a_value: Optional[v22a_api.SubscriberToNotify] = None + v19_value: v19_api.SubscriberToNotify | None = None + v22a_value: v22a_api.SubscriberToNotify | None = None @property def rid_version(self) -> RIDVersion: @@ -247,7 +246,7 @@ def rid_version(self) -> RIDVersion: @property def raw( self, - ) -> Union[v19_api.SubscriberToNotify, v22a_api.SubscriberToNotify]: + ) -> v19_api.SubscriberToNotify | v22a_api.SubscriberToNotify: if self.rid_version == RIDVersion.f3411_19: return self.v19_value elif self.rid_version == RIDVersion.f3411_22a: @@ -261,8 +260,8 @@ def notify( self, isa_id: str, utm_session: infrastructure.UTMClientSession, - isa: Optional[ISA] = None, - participant_id: Optional[str] = None, + isa: ISA | None = None, + participant_id: str | None = None, ) -> ISAChangeNotification: # Note that optional `extents` are not specified if self.rid_version == RIDVersion.f3411_19: @@ -315,7 +314,7 @@ def url(self) -> str: class ChangedISA(RIDQuery): """Version-independent representation of a changed F3411 identification service area.""" - mutation: Optional[str] = None + mutation: str | None = None @property def _v19_response( @@ -336,13 +335,13 @@ def _v22a_response( ) @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: # Tolerate reasonable-but-technically-incorrect code 201 if not ( self.status_code == 200 or (self.mutation == "create" and self.status_code == 201) ): - return ["Failed to mutate ISA ({})".format(self.status_code)] + return [f"Failed to mutate ISA ({self.status_code})"] if self.query.response.json is None: return ["ISA response did not include valid JSON"] @@ -384,7 +383,7 @@ def isa(self) -> ISA: ) @property - def subscribers(self) -> Optional[List[SubscriberToNotify]]: + def subscribers(self) -> list[SubscriberToNotify] | None: if self.rid_version == RIDVersion.f3411_19: if ( "subscribers" not in self._v19_response @@ -410,7 +409,7 @@ def subscribers(self) -> Optional[List[SubscriberToNotify]]: ) @property - def sub_ids(self) -> Set[str]: + def sub_ids(self) -> set[str]: if self.rid_version == RIDVersion.f3411_19: return set( [ @@ -440,24 +439,24 @@ class ISAChange(ImplicitDict): dss_query: ChangedISA - notifications: Dict[str, ISAChangeNotification] + notifications: dict[str, ISAChangeNotification] """Mapping from USS base URL to change notification query""" @property - def subscribers(self) -> Optional[List[SubscriberToNotify]]: + def subscribers(self) -> list[SubscriberToNotify] | None: """List of subscribers that required a notification for the change.""" return self.dss_query.subscribers def build_isa_request_body( - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, start_time: datetime.datetime, 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 +489,7 @@ def build_isa_request_body( def build_isa_url( - rid_version: RIDVersion, isa_id: str, isa_version: Optional[str] = None + rid_version: RIDVersion, isa_id: str, isa_version: str | None = None ) -> (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. @@ -520,7 +519,7 @@ def build_isa_url( def put_isa( - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, start_time: datetime.datetime, @@ -529,9 +528,9 @@ def put_isa( isa_id: str, rid_version: RIDVersion, utm_client: infrastructure.UTMClientSession, - isa_version: Optional[str] = None, - participant_id: Optional[str] = None, - do_not_notify: Optional[Union[str, List[str]]] = None, + isa_version: str | None = None, + participant_id: str | None = None, + do_not_notify: str | list[str] | None = None, ) -> ISAChange: is_creation = isa_version is None mutation = "create" if is_creation else "update" @@ -606,8 +605,8 @@ def delete_isa( isa_version: str, rid_version: RIDVersion, utm_client: infrastructure.UTMClientSession, - participant_id: Optional[str] = None, - do_not_notify: Optional[Union[str, List[str]]] = None, + participant_id: str | None = None, + do_not_notify: str | list[str] | None = None, ) -> ISAChange: if rid_version == RIDVersion.f3411_19: op = v19_api.OPERATIONS[v19_api.OperationID.DeleteIdentificationServiceArea] @@ -679,7 +678,7 @@ def _v22a_request( ) @property - def isa(self) -> Optional[ISA]: + def isa(self) -> ISA | None: if self.rid_version == RIDVersion.f3411_19: return ISA( v19_value=( diff --git a/monitoring/monitorlib/mutate/scd.py b/monitoring/monitorlib/mutate/scd.py index caedc3186e..de168dc7ba 100644 --- a/monitoring/monitorlib/mutate/scd.py +++ b/monitoring/monitorlib/mutate/scd.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional import s2sphere import yaml @@ -20,25 +19,21 @@ class MutatedSubscription(fetch.Query): - mutation: Optional[str] = None + mutation: str | None = None @property def success(self) -> bool: return not self.errors @property - def errors(self) -> List[str]: + def errors(self) -> list[str]: if self.status_code != 200: - return [ - "Failed to {} SCD Subscription ({})".format( - self.mutation, self.status_code - ) - ] + return [f"Failed to {self.mutation} SCD Subscription ({self.status_code})"] if self.json_result is None: return ["Response did not contain valid JSON"] @property - def subscription(self) -> Optional[Subscription]: + def subscription(self) -> Subscription | None: if self.json_result is None: return None try: @@ -52,7 +47,7 @@ def subscription(self) -> Optional[Subscription]: return None @property - def operational_intent_references(self) -> List[OperationalIntentReference]: + def operational_intent_references(self) -> list[OperationalIntentReference]: if self.json_result is None: return [] try: @@ -82,8 +77,8 @@ def upsert_subscription( notify_for_constraints: bool, min_alt_m: float = 0, max_alt_m: float = 3048, - version: Optional[str] = None, - participant_id: Optional[str] = None, + version: str | None = None, + participant_id: str | None = None, ) -> MutatedSubscription: is_creation = version is None if is_creation: @@ -123,7 +118,7 @@ def upsert_subscription( def build_upsert_subscription_params( area_vertices: s2sphere.LatLngRect, - start_time: Optional[datetime.datetime], + start_time: datetime.datetime | None, end_time: datetime.datetime, base_url: str, notify_for_op_intents: bool, @@ -149,7 +144,7 @@ def delete_subscription( utm_client: infrastructure.UTMClientSession, subscription_id: str, version: str, - participant_id: Optional[str] = None, + participant_id: str | None = None, ) -> MutatedSubscription: op = OPERATIONS[OperationID.DeleteSubscription] result = MutatedSubscription( diff --git a/monitoring/monitorlib/rid.py b/monitoring/monitorlib/rid.py index 8cb5fed784..decb400d29 100644 --- a/monitoring/monitorlib/rid.py +++ b/monitoring/monitorlib/rid.py @@ -90,7 +90,7 @@ def realtime_period(self) -> timedelta: elif self == RIDVersion.f3411_22a: return timedelta(seconds=v22a_constants.NetMaxNearRealTimeDataPeriodSeconds) else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def max_diagonal_km(self) -> float: @@ -99,7 +99,7 @@ def max_diagonal_km(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetMaxDisplayAreaDiagonalKm else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def max_details_diagonal_km(self) -> float: @@ -108,7 +108,7 @@ def max_details_diagonal_km(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDetailsMaxDisplayAreaDiagonalKm else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def min_cluster_size_percent(self) -> float: @@ -117,7 +117,7 @@ def min_cluster_size_percent(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetMinClusterSizePercent else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def min_obfuscation_distance_m(self) -> float: @@ -126,7 +126,7 @@ def min_obfuscation_distance_m(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetMinObfuscationDistanceM else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def short_name(self) -> str: @@ -144,7 +144,7 @@ def min_session_length_s(self) -> int: elif self == RIDVersion.f3411_22a: return v22a_constants.NetMinSessionLengthSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_init_resp_percentile95_s(self) -> float: @@ -153,7 +153,7 @@ def dp_init_resp_percentile95_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpInitResponse95thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_init_resp_percentile99_s(self) -> float: @@ -162,7 +162,7 @@ def dp_init_resp_percentile99_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpInitResponse99thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_data_resp_percentile95_s(self) -> float: @@ -171,7 +171,7 @@ def dp_data_resp_percentile95_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpDataResponse95thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_details_resp_percentile95_s(self) -> float: @@ -180,7 +180,7 @@ def dp_details_resp_percentile95_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpDetailsResponse95thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_details_resp_percentile99_s(self) -> float: @@ -189,7 +189,7 @@ def dp_details_resp_percentile99_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpDetailsResponse99thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dp_data_resp_percentile99_s(self) -> float: @@ -198,7 +198,7 @@ def dp_data_resp_percentile99_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDpDataResponse99thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def sp_data_resp_percentile95_s(self) -> float: @@ -207,7 +207,7 @@ def sp_data_resp_percentile95_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetSpDataResponseTime95thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def sp_data_resp_percentile99_s(self) -> float: @@ -216,7 +216,7 @@ def sp_data_resp_percentile99_s(self) -> float: elif self == RIDVersion.f3411_22a: return v22a_constants.NetSpDataResponseTime99thPercentileSeconds else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def dss_max_subscriptions_per_area(self) -> int: @@ -225,7 +225,7 @@ def dss_max_subscriptions_per_area(self) -> int: elif self == RIDVersion.f3411_22a: return v22a_constants.NetDSSMaxSubscriptionPerArea else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") @property def min_altitude_api(self) -> int: @@ -243,7 +243,7 @@ def flights_url_of(self, base_url: str) -> str: flights_path = v22a_api.OPERATIONS[v22a_api.OperationID.SearchFlights].path return base_url + flights_path else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") def scope_dp(self) -> str: if self == RIDVersion.f3411_19: @@ -251,7 +251,7 @@ def scope_dp(self) -> str: elif self == RIDVersion.f3411_22a: return v22a_constants.Scope.DisplayProvider else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") def scope_sp(self) -> str: if self == RIDVersion.f3411_19: @@ -259,4 +259,4 @@ def scope_sp(self) -> str: elif self == RIDVersion.f3411_22a: return v22a_constants.Scope.ServiceProvider else: - raise ValueError("Unsupported RID version '{}'".format(self)) + raise ValueError(f"Unsupported RID version '{self}'") diff --git a/monitoring/monitorlib/rid_automated_testing/injection_api.py b/monitoring/monitorlib/rid_automated_testing/injection_api.py index 90857d54db..ee99c05146 100644 --- a/monitoring/monitorlib/rid_automated_testing/injection_api.py +++ b/monitoring/monitorlib/rid_automated_testing/injection_api.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional, Tuple import arrow import s2sphere @@ -31,7 +30,7 @@ class TestFlight(injection.TestFlight): - raw_telemetry: Optional[List[RIDAircraftState]] + raw_telemetry: list[RIDAircraftState] | None """Copy of original telemetry with potential invalid data""" def __init__(self, *args, **kwargs): @@ -119,7 +118,7 @@ def __init__(self, *args, **kwargs): def get_span( self, - ) -> Tuple[Optional[datetime.datetime], Optional[datetime.datetime]]: + ) -> tuple[datetime.datetime | None, datetime.datetime | None]: earliest = None latest = None times = [ @@ -137,8 +136,8 @@ def get_span( latest = t return (earliest, latest) - def get_details(self, t_now: datetime.datetime) -> Optional[RIDFlightDetails]: - latest_after: Optional[datetime.datetime] = None + def get_details(self, t_now: datetime.datetime) -> RIDFlightDetails | None: + latest_after: datetime.datetime | None = None tf_details = None for response in self.details_responses: t_response = arrow.get(response.effective_after).datetime @@ -148,7 +147,7 @@ def get_details(self, t_now: datetime.datetime) -> Optional[RIDFlightDetails]: tf_details = response.details return tf_details - def get_id(self, t_now: datetime.datetime) -> Optional[str]: + def get_id(self, t_now: datetime.datetime) -> str | None: details = self.get_details(t_now) return details.id if details else None @@ -174,8 +173,8 @@ def order_telemetry(self): def select_relevant_states( self, view: s2sphere.LatLngRect, t0: datetime.datetime, t1: datetime.datetime - ) -> List[RIDAircraftState]: - recent_states: List[RIDAircraftState] = [] + ) -> list[RIDAircraftState]: + recent_states: list[RIDAircraftState] = [] previously_outside = False previously_inside = False previous_telemetry = None @@ -201,12 +200,12 @@ def select_relevant_states( previous_telemetry = telemetry return recent_states - def get_rect(self) -> Optional[s2sphere.LatLngRect]: + def get_rect(self) -> s2sphere.LatLngRect | None: return geo.bounding_rect( [(t.position.lat, t.position.lng) for t in self.telemetry] ) - def get_mean_update_rate_hz(self) -> Optional[float]: + def get_mean_update_rate_hz(self) -> float | None: """ Calculate the mean update rate of the telemetry in Hz """ @@ -222,7 +221,7 @@ def get_mean_update_rate_hz(self) -> Optional[float]: class CreateTestParameters(injection.CreateTestParameters): def get_span( self, - ) -> Tuple[Optional[datetime.datetime], Optional[datetime.datetime]]: + ) -> tuple[datetime.datetime | None, datetime.datetime | None]: if not self.requested_flights: return (None, None) (earliest, latest) = (None, None) @@ -235,7 +234,7 @@ def get_span( latest = t1 return (earliest, latest) - def get_rect(self) -> Optional[s2sphere.LatLngRect]: + def get_rect(self) -> s2sphere.LatLngRect | None: result = None for flight in self.requested_flights: flight = TestFlight(flight) diff --git a/monitoring/monitorlib/rid_v1.py b/monitoring/monitorlib/rid_v1.py index 6e16bff877..a22999576d 100644 --- a/monitoring/monitorlib/rid_v1.py +++ b/monitoring/monitorlib/rid_v1.py @@ -1,5 +1,4 @@ import datetime -from typing import Dict, List, Optional import s2sphere from implicitdict import ImplicitDict, StringBasedDateTime @@ -11,20 +10,20 @@ UPP2_SCOPE_ENHANCED_DETAILS = "rid.read.enhanced_details" -def geo_polygon_string(vertices: List[Dict[str, float]]) -> str: +def geo_polygon_string(vertices: list[dict[str, float]]) -> str: return ",".join("{},{}".format(v["lat"], v["lng"]) for v in vertices) -def geo_polygon_string_from_s2(vertices: List[s2sphere.LatLng]) -> str: - return ",".join("{},{}".format(v.lat().degrees, v.lng().degrees) for v in vertices) +def geo_polygon_string_from_s2(vertices: list[s2sphere.LatLng]) -> str: + return ",".join(f"{v.lat().degrees},{v.lng().degrees}" for v in vertices) def make_volume_4d( - vertices: List[s2sphere.LatLng], + vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, ) -> Volume4D: return ImplicitDict.parse( { @@ -55,22 +54,22 @@ def make_volume_4d( class ISA(dict): @property - def errors(self) -> List[str]: - errors: List[str] = [] + def errors(self) -> list[str]: + errors: list[str] = [] if "flights_url" not in self: errors.append("flights_url field missing") return errors @property - def id(self) -> Optional[str]: + def id(self) -> str | None: return self.get("id", None) @property - def owner(self) -> Optional[str]: + def owner(self) -> str | None: return self.get("owner", None) @property - def flights_url(self) -> Optional[str]: + def flights_url(self) -> str | None: return self.get("flights_url", None) @@ -94,5 +93,5 @@ def valid(self) -> bool: return True @property - def version(self) -> Optional[str]: + def version(self) -> str | None: return self.get("version", None) diff --git a/monitoring/monitorlib/rid_v2.py b/monitoring/monitorlib/rid_v2.py index 527ea3714b..418871e5ec 100644 --- a/monitoring/monitorlib/rid_v2.py +++ b/monitoring/monitorlib/rid_v2.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional import s2sphere from implicitdict import ImplicitDict, StringBasedDateTime @@ -25,11 +24,11 @@ def make_lat_lng_point_from_s2(point: s2sphere.LatLng) -> LatLngPoint: def make_volume_4d( - vertices: List[s2sphere.LatLng], + vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, ) -> Volume4D: return ImplicitDict.parse( { diff --git a/monitoring/monitorlib/scd.py b/monitoring/monitorlib/scd.py index 868598f72e..7be2ed0392 100644 --- a/monitoring/monitorlib/scd.py +++ b/monitoring/monitorlib/scd.py @@ -1,5 +1,4 @@ import base64 -from typing import Dict, List, Optional from implicitdict import StringBasedDateTime from uas_standards.astm.f3548.v21.api import ( @@ -35,7 +34,7 @@ def priority_of(details: OperationalIntentDetails) -> int: def make_exchange_record(query: Query, msg_problem: str) -> ExchangeRecord: - def str_headers(headers: Optional[Dict[str, str]]) -> List[str]: + def str_headers(headers: dict[str, str] | None) -> list[str]: if headers is None: return [] return [f"{h_name}: {h_val}" for h_name, h_val in headers.items()] diff --git a/monitoring/monitorlib/schema_validation.py b/monitoring/monitorlib/schema_validation.py index 31343cfb78..a355e69879 100644 --- a/monitoring/monitorlib/schema_validation.py +++ b/monitoring/monitorlib/schema_validation.py @@ -2,7 +2,6 @@ from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Dict, List, Type import bc_jsonpath_ng import jsonschema.validators @@ -78,18 +77,18 @@ class F3548_21(str, Enum): ) -_openapi_content_cache: Dict[str, dict] = {} +_openapi_content_cache: dict[str, dict] = {} def _get_openapi_content(openapi_path: str) -> dict: if openapi_path not in _openapi_content_cache: - with open(openapi_path, "r") as f: + with open(openapi_path) as f: _openapi_content_cache[openapi_path] = yaml.full_load(f) return _openapi_content_cache[openapi_path] @dataclass -class ValidationError(object): +class ValidationError: """Error encountered while validating an instance against a schema.""" message: str @@ -99,7 +98,7 @@ class ValidationError(object): """Location of the data causing the validation error.""" -def _collect_errors(e: jsonschema.ValidationError) -> List[ValidationError]: +def _collect_errors(e: jsonschema.ValidationError) -> list[ValidationError]: if e.context: result = [] for child in e.context: @@ -111,7 +110,7 @@ def _collect_errors(e: jsonschema.ValidationError) -> List[ValidationError]: def validate( openapi_path: str, object_path: str, instance: dict -) -> List[ValidationError]: +) -> list[ValidationError]: """Validate an object instance against the OpenAPI schema definition for that object type. Args: @@ -157,8 +156,8 @@ def validate( return result -def _definitions_resolver(t: Type) -> SchemaVars: - def path_to(t_dest: Type, t_src: Type) -> str: +def _definitions_resolver(t: type) -> SchemaVars: + def path_to(t_dest: type, t_src: type) -> str: return "#/definitions/" + ( t_dest.__module__ + "." + t_dest.__qualname__ ).replace(".", "_") @@ -168,7 +167,7 @@ def path_to(t_dest: Type, t_src: Type) -> str: return SchemaVars(name=full_name, path_to=path_to) -def _make_implicitdict_schema(t: Type[ImplicitDict]) -> dict: +def _make_implicitdict_schema(t: type[ImplicitDict]) -> dict: repo = {} make_json_schema(t, _definitions_resolver, repo) config_vars = _definitions_resolver(t) @@ -178,8 +177,8 @@ def _make_implicitdict_schema(t: Type[ImplicitDict]) -> dict: def validate_implicitdict_object( - obj: dict, t: Type[ImplicitDict] -) -> List[ValidationError]: + obj: dict, t: type[ImplicitDict] +) -> list[ValidationError]: schema = _make_implicitdict_schema(t) jsonschema.Draft202012Validator.check_schema(schema) validator = jsonschema.Draft202012Validator(schema) diff --git a/monitoring/monitorlib/subscription_params.py b/monitoring/monitorlib/subscription_params.py index 03f5020fad..65906a36e9 100644 --- a/monitoring/monitorlib/subscription_params.py +++ b/monitoring/monitorlib/subscription_params.py @@ -1,7 +1,7 @@ from __future__ import annotations import datetime -from typing import List, Optional, Self +from typing import Self import s2sphere from implicitdict import ImplicitDict @@ -19,16 +19,16 @@ class SubscriptionParams(ImplicitDict): sub_id: str """Uniquely identifies the subscription""" - area_vertices: List[LatLngPoint] + area_vertices: list[LatLngPoint] """List of vertices of a polygon defining the area of interest""" - min_alt_m: Optional[float] + min_alt_m: float | None """Minimum altitude in meters""" - max_alt_m: Optional[float] + max_alt_m: float | None """Maximum altitude in meters""" - start_time: Optional[datetime.datetime] = None + start_time: datetime.datetime | None = None """Start time of subscription""" end_time: datetime.datetime diff --git a/monitoring/monitorlib/temporal.py b/monitoring/monitorlib/temporal.py index 0a44811792..73273c0295 100644 --- a/monitoring/monitorlib/temporal.py +++ b/monitoring/monitorlib/temporal.py @@ -2,7 +2,6 @@ from datetime import datetime, timedelta from enum import Enum -from typing import Dict, List, Optional import arrow from implicitdict import ImplicitDict, StringBasedDateTime, StringBasedTimeDelta @@ -59,7 +58,7 @@ class NextDay(ImplicitDict): * "-08:00" (ISO time zone) * "US/Pacific" (IANA time zone)""" - days_of_the_week: Optional[List[DayOfTheWeek]] = None + days_of_the_week: list[DayOfTheWeek] | None = None """Acceptable days of the week. Omit to indicate that any day of the week is acceptable.""" @@ -77,32 +76,32 @@ class TimeDuringTest(str, Enum): class TestTime(ImplicitDict): """Exactly one of the time option fields of this object must be specified.""" - absolute_time: Optional[StringBasedDateTime] = None + absolute_time: StringBasedDateTime | None = None """Time option field to use a precise timestamp which does not change with test conditions. The value of absolute_time is limited given that the specific time a test will be started is unknown, and the jurisdictions usually impose a limit on how far in the future an operation can be planned. """ - time_during_test: Optional[TimeDuringTest] = None + time_during_test: TimeDuringTest | None = None """Time option field to, if specified, use a timestamp relating to the current test run.""" - next_day: Optional[NextDay] = None + next_day: NextDay | None = None """Time option field to use a timestamp equal to midnight beginning the next occurrence of any matching day following the specified reference timestamp.""" - next_sun_position: Optional[NextSunPosition] = None + next_sun_position: NextSunPosition | None = None """Time option field to use a timestamp equal to the next time after the specified reference timestamp at which the sun will be at the specified angle above the horizon.""" - offset_from: Optional[OffsetTime] = None + offset_from: OffsetTime | None = None """Time option field to use a timestamp that is offset by the specified amount from the specified time.""" - use_timezone: Optional[str] = None + use_timezone: str | None = None """If specified, report the timestamp in the specified time zone. Examples: * "local" (local time of machine running this code) * "Z" (Zulu time) * "-08:00" (ISO time zone) * "US/Pacific" (IANA time zone)""" - def resolve(self, times: Dict[TimeDuringTest, Time]) -> Time: + def resolve(self, times: dict[TimeDuringTest, Time]) -> Time: """Resolve TestTime into specific Time.""" result = None if self.absolute_time is not None: diff --git a/monitoring/monitorlib/testing.py b/monitoring/monitorlib/testing.py index 7e7616ace8..30a082ddf5 100644 --- a/monitoring/monitorlib/testing.py +++ b/monitoring/monitorlib/testing.py @@ -1,6 +1,5 @@ import inspect import os -from typing import Optional from monitoring.monitorlib.formatting import make_datetime @@ -10,14 +9,14 @@ def assert_datetimes_are_equal(t1, t2, tolerance_seconds: float = 0) -> None: t1_datetime = make_datetime(t1) t2_datetime = make_datetime(t2) except ValueError as e: - assert False, "Error interpreting value as datetime: {}".format(e) + assert False, f"Error interpreting value as datetime: {e}" if tolerance_seconds == 0: assert t1_datetime == t2_datetime else: assert abs((t1_datetime - t2_datetime).total_seconds()) < tolerance_seconds -def make_fake_url(suffix: Optional[str] = None, frames_above: int = 1) -> str: +def make_fake_url(suffix: str | None = None, frames_above: int = 1) -> str: """Create a dummy URL revealing the location from which this function was called. The URL generated is a function solely of the file from which this function is called and the provided suffix. diff --git a/monitoring/monitorlib/transformations.py b/monitoring/monitorlib/transformations.py index 8286fe658b..0867d87ede 100644 --- a/monitoring/monitorlib/transformations.py +++ b/monitoring/monitorlib/transformations.py @@ -1,24 +1,22 @@ -from typing import Optional - from implicitdict import ImplicitDict class RelativeTranslation(ImplicitDict): """Offset a geo feature by a particular amount.""" - meters_east: Optional[float] + meters_east: float | None """Number of meters east to translate.""" - meters_north: Optional[float] + meters_north: float | None """Number of meters north to translate.""" - meters_up: Optional[float] + meters_up: float | None """Number of meters upward to translate.""" - degrees_east: Optional[float] + degrees_east: float | None """Number of degrees of longitude east to translate.""" - degrees_north: Optional[float] + degrees_north: float | None """Number of degrees of latitude north to translate.""" @@ -35,6 +33,6 @@ class AbsoluteTranslation(ImplicitDict): class Transformation(ImplicitDict): """A transformation to apply to a geotemporal feature. Exactly one field must be specified.""" - relative_translation: Optional[RelativeTranslation] + relative_translation: RelativeTranslation | None - absolute_translation: Optional[AbsoluteTranslation] + absolute_translation: AbsoluteTranslation | None diff --git a/monitoring/monitorlib/uspace.py b/monitoring/monitorlib/uspace.py index 10d5e77f8c..d9aaff19db 100644 --- a/monitoring/monitorlib/uspace.py +++ b/monitoring/monitorlib/uspace.py @@ -1,4 +1,3 @@ -from typing import List from urllib.parse import urlparse from uas_standards.ansi_cta_2063_a import SerialNumber @@ -13,8 +12,8 @@ def problems_with_flight_authorisation( flight_auth: FlightAuthorisationData, -) -> List[str]: - problems: List[str] = [] +) -> list[str]: + problems: list[str] = [] if not SerialNumber(flight_auth.uas_serial_number).valid: problems.append("Invalid serial number") if not OperatorRegistrationNumber(flight_auth.operator_id).valid: diff --git a/monitoring/monitorlib/versioning.py b/monitoring/monitorlib/versioning.py index 477021be73..5499c0db99 100644 --- a/monitoring/monitorlib/versioning.py +++ b/monitoring/monitorlib/versioning.py @@ -1,14 +1,13 @@ import inspect import os import subprocess -from typing import Optional import monitoring -_commit_hash: Optional[str] = None -_code_version: Optional[str] = None -_github_base_url: Optional[str] = None -_repo_root: Optional[str] = None +_commit_hash: str | None = None +_code_version: str | None = None +_github_base_url: str | None = None +_repo_root: str | None = None def repo_url_of(abspath: str) -> str: diff --git a/monitoring/prober/aux_/test_token_validation.py b/monitoring/prober/aux_/test_token_validation.py index 24fea27635..56daa45c27 100644 --- a/monitoring/prober/aux_/test_token_validation.py +++ b/monitoring/prober/aux_/test_token_validation.py @@ -13,7 +13,7 @@ def test_validate(aux_session): @default_scope(Scope.Read) def test_validate_token_good_user(aux_session, subscriber): - resp = aux_session.get("/validate_oauth?owner={}".format(subscriber)) + resp = aux_session.get(f"/validate_oauth?owner={subscriber}") assert resp.status_code == 200 diff --git a/monitoring/prober/conftest.py b/monitoring/prober/conftest.py index 88e2ef4e2c..193cd47ba9 100644 --- a/monitoring/prober/conftest.py +++ b/monitoring/prober/conftest.py @@ -1,5 +1,5 @@ import argparse -from typing import Callable, Optional +from collections.abc import Callable import pytest import uas_standards.astm.f3411.v19.constants as v19_constants @@ -107,8 +107,8 @@ def pytest_runtest_makereport(item, call): def make_session( - pytestconfig, endpoint_suffix: str, auth_option: Optional[str] = None -) -> Optional[UTMClientSession]: + pytestconfig, endpoint_suffix: str, auth_option: str | None = None +) -> UTMClientSession | None: dss_endpoint = pytestconfig.getoption("dss_endpoint") if dss_endpoint is None: pytest.skip("dss-endpoint option not set") @@ -117,7 +117,7 @@ def make_session( if auth_option: auth_spec = pytestconfig.getoption(auth_option) if not auth_spec: - pytest.skip("%s option not set" % auth_option) + pytest.skip(f"{auth_option} option not set") auth_adapter = auth.make_auth_adapter(auth_spec) s = UTMClientSession(dss_endpoint + endpoint_suffix, auth_adapter) @@ -125,8 +125,8 @@ def make_session( def make_session_async( - pytestconfig, endpoint_suffix: str, auth_option: Optional[str] = None -) -> Optional[AsyncUTMTestSession]: + pytestconfig, endpoint_suffix: str, auth_option: str | None = None +) -> AsyncUTMTestSession | None: dss_endpoint = pytestconfig.getoption("dss_endpoint") if dss_endpoint is None: pytest.skip("dss-endpoint option not set") @@ -135,7 +135,7 @@ def make_session_async( if auth_option: auth_spec = pytestconfig.getoption(auth_option) if not auth_spec: - pytest.skip("%s option not set" % auth_option) + pytest.skip(f"{auth_option} option not set") auth_adapter = auth.make_auth_adapter(auth_spec) s = AsyncUTMTestSession(dss_endpoint + endpoint_suffix, auth_adapter) @@ -194,7 +194,7 @@ def scd_session2(pytestconfig) -> UTMClientSession: @pytest.fixture() -def subscriber(pytestconfig) -> Optional[str]: +def subscriber(pytestconfig) -> str | None: """Subscriber of USS making UTM API calls""" if pytestconfig.getoption(OPT_RID_AUTH): session = make_session(pytestconfig, BASE_URL_RID, OPT_RID_AUTH) diff --git a/monitoring/prober/decode_id.py b/monitoring/prober/decode_id.py index 236fab1415..bf2c4b10de 100644 --- a/monitoring/prober/decode_id.py +++ b/monitoring/prober/decode_id.py @@ -2,7 +2,6 @@ import importlib import pkgutil import sys -from typing import List import monitoring from monitoring.prober import infrastructure @@ -30,7 +29,7 @@ def import_submodules(package, recursive=True): import_submodules(monitoring.prober) -def parse_args(argv: List[str]): +def parse_args(argv: list[str]): parser = argparse.ArgumentParser(description="Decode a test ID") parser.add_argument( action="store", dest="id", type=str, metavar="ID", help="The ID to decode" @@ -41,12 +40,10 @@ def parse_args(argv: List[str]): if __name__ == "__main__": args = parse_args(sys.argv[1:]) owner_name, id_code = infrastructure.IDFactory.decode(args.id) - print("Owner: {}".format(owner_name)) + print(f"Owner: {owner_name}") if id_code in infrastructure.resource_type_code_descriptions: print( - "Resource type: {}".format( - infrastructure.resource_type_code_descriptions[id_code] - ) + f"Resource type: {infrastructure.resource_type_code_descriptions[id_code]}" ) else: - print("Resource type: Unregistered ({})".format(id_code)) + print(f"Resource type: Unregistered ({id_code})") diff --git a/monitoring/prober/infrastructure.py b/monitoring/prober/infrastructure.py index 0a5a96ffdf..a505e96c71 100644 --- a/monitoring/prober/infrastructure.py +++ b/monitoring/prober/infrastructure.py @@ -1,7 +1,6 @@ import functools import inspect import os -from typing import Dict, Tuple import pytest @@ -37,9 +36,7 @@ def wrapper_default_scope(*args, **kwargs): for prerequisite in prerequisites: if prerequisite.__name__ not in module_results: pytest.fail( - "Prerequisite test {} did not exist when evaluating the dependent test {}".format( - prerequisite.__name__, func.__name__ - ) + f"Prerequisite test {prerequisite.__name__} did not exist when evaluating the dependent test {func.__name__}" ) if not module_results[prerequisite.__name__].passed: pytest.skip("Prerequisite task did not pass") @@ -88,7 +85,7 @@ def wrapper_default_scope(*args, **kwargs): if api_version in acceptable_versions: return func(*args, **kwargs) else: - pytest.skip("Not applicable for API version {}".format(api_version)) + pytest.skip(f"Not applicable for API version {api_version}") return wrapper_default_scope @@ -96,7 +93,7 @@ def wrapper_default_scope(*args, **kwargs): ResourceType = int -resource_type_code_descriptions: Dict[ResourceType, str] = {} +resource_type_code_descriptions: dict[ResourceType, str] = {} # Next code: 404 @@ -115,18 +112,16 @@ def register_resource_type(code: int, description: str) -> ResourceType: test_filename = inspect.stack()[1].filename this_folder = os.path.dirname(os.path.abspath(__file__)) test = test_filename[len(this_folder) + 1 :] - full_description = "{}: {}".format(test, description) + full_description = f"{test}: {description}" if code in resource_type_code_descriptions: raise ValueError( - 'Resource type code {} is already in use as "{}" so it cannot be used for "{}"'.format( - code, resource_type_code_descriptions[code], full_description - ) + f'Resource type code {code} is already in use as "{resource_type_code_descriptions[code]}" so it cannot be used for "{full_description}"' ) resource_type_code_descriptions[code] = full_description return code -class IDFactory(object): +class IDFactory: """Creates UUIDv4 (as described in RFC4122) formatted IDs encoding the kind of ID and owner. Format: 0000XXXX-YYYY-4ZYY-8YYY-YYYYYYYY0000 @@ -149,29 +144,19 @@ def __init__(self, test_owner: str): def make_id(self, resource_type: ResourceType): """Make a test ID with the specified resource type code""" - return "0000{x}-{y1}-40{y2}-8{y3}-{y4}0000".format( - x=utils.encode_resource_type_code(resource_type), - y1=self.owner_id[0:4], - y2=self.owner_id[4:6], - y3=self.owner_id[6:9], - y4=self.owner_id[9:17], - ) + return f"0000{utils.encode_resource_type_code(resource_type)}-{self.owner_id[0:4]}-40{self.owner_id[4:6]}-8{self.owner_id[6:9]}-{self.owner_id[9:17]}0000" @classmethod - def decode(cls, id: str) -> Tuple[str, ResourceType]: + def decode(cls, id: str) -> tuple[str, ResourceType]: hex_digits = id.replace("-", "") if len(hex_digits) != 32: - raise ValueError( - "ID {} has the wrong number of characters for a UUID".format(id) - ) + raise ValueError(f"ID {id} has the wrong number of characters for a UUID") if hex_digits[0:4] != "0000" or hex_digits[-4:] != "0000": raise ValueError( - "ID {} does not have the leading and trailing zeros indicating a test ID".format( - id - ) + f"ID {id} does not have the leading and trailing zeros indicating a test ID" ) if hex_digits[12:14] != "40": - raise ValueError("ID {} is not formatted like a v4 test ID".format(id)) + raise ValueError(f"ID {id} is not formatted like a v4 test ID") x = hex_digits[4:8] y = hex_digits[8:12] + hex_digits[14:28] resource_type_code = ResourceType(int(x, 16)) diff --git a/monitoring/prober/scd/actions.py b/monitoring/prober/scd/actions.py index c26d6a10f9..4c57b57fa4 100644 --- a/monitoring/prober/scd/actions.py +++ b/monitoring/prober/scd/actions.py @@ -7,13 +7,13 @@ def _read_both_scope(scd_api: str) -> str: if scd_api == scd.API_0_3_17: return f"{str(SCOPE_SC)} {str(SCOPE_CP)}" else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") def delete_constraint_reference_if_exists( id: str, scd_session: UTMClientSession, scd_api: str ): - resp = scd_session.get("/constraint_references/{}".format(id), scope=SCOPE_CM) + resp = scd_session.get(f"/constraint_references/{id}", scope=SCOPE_CM) if resp.status_code == 200: if scd_api == scd.API_0_3_17: existing_constraint = resp.json().get("constraint_reference", None) @@ -22,8 +22,8 @@ def delete_constraint_reference_if_exists( scope=SCOPE_CM, ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) - assert resp.status_code == 200, "{}: {}".format(resp.url, resp.content) + raise NotImplementedError(f"Unsupported API version {scd_api}") + assert resp.status_code == 200, f"{resp.url}: {resp.content}" elif resp.status_code == 404: # As expected. pass @@ -34,7 +34,7 @@ def delete_constraint_reference_if_exists( def delete_subscription_if_exists( sub_id: str, scd_session: UTMClientSession, scd_api: str ): - resp = scd_session.get("/subscriptions/{}".format(sub_id), scope=SCOPE_SC) + resp = scd_session.get(f"/subscriptions/{sub_id}", scope=SCOPE_SC) if resp.status_code == 200: if scd_api == scd.API_0_3_17: sub = resp.json().get("subscription", None) @@ -43,7 +43,7 @@ def delete_subscription_if_exists( scope=_read_both_scope(scd_api), ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content elif resp.status_code == 404: # As expected. @@ -56,17 +56,15 @@ def delete_operation_if_exists(id: str, scd_session: UTMClientSession, scd_api: if scd_api == scd.API_0_3_17: url = "/operational_intent_references/{}" else: - assert False, "Unsupported API {}".format(scd_api) + assert False, f"Unsupported API {scd_api}" resp = scd_session.get(url.format(id), scope=SCOPE_SC) if resp.status_code == 200: if scd_api == scd.API_0_3_17: ovn = resp.json()["operational_intent_reference"]["ovn"] - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(id, ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{id}/{ovn}") assert resp.status_code == 200, resp.content elif resp.status_code == 404: # As expected. pass else: - assert False, "Unsupported API {}".format(scd_api) + assert False, f"Unsupported API {scd_api}" diff --git a/monitoring/prober/scd/test_constraint_simple.py b/monitoring/prober/scd/test_constraint_simple.py index 8610d8e285..82be373cdf 100644 --- a/monitoring/prober/scd/test_constraint_simple.py +++ b/monitoring/prober/scd/test_constraint_simple.py @@ -63,7 +63,7 @@ def test_constraint_does_not_exist_get(ids, scd_api, scd_session): for scope in auths: resp = scd_session.get( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), scope=scope + f"/constraint_references/{ids(CONSTRAINT_TYPE)}", scope=scope ) assert resp.status_code == 404, resp.content @@ -99,9 +99,7 @@ def test_constraint_does_not_exist_query(ids, scd_api, scd_session): def test_create_constraint_single_extent(ids, scd_api, scd_session): req = _make_c1_request() req["extents"] = req["extents"][0] - resp = scd_session.put( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), json=req - ) + resp = scd_session.put(f"/constraint_references/{ids(CONSTRAINT_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -111,9 +109,7 @@ def test_create_constraint_single_extent(ids, scd_api, scd_session): def test_create_constraint_missing_time_start(ids, scd_api, scd_session): req = _make_c1_request() del req["extents"][0]["time_start"] - resp = scd_session.put( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), json=req - ) + resp = scd_session.put(f"/constraint_references/{ids(CONSTRAINT_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -123,9 +119,7 @@ def test_create_constraint_missing_time_start(ids, scd_api, scd_session): def test_create_constraint_missing_time_end(ids, scd_api, scd_session): req = _make_c1_request() del req["extents"][0]["time_end"] - resp = scd_session.put( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), json=req - ) + resp = scd_session.put(f"/constraint_references/{ids(CONSTRAINT_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -135,19 +129,13 @@ def test_create_constraint(ids, scd_api, scd_session): id = ids(CONSTRAINT_TYPE) req = _make_c1_request() - resp = scd_session.put( - "/constraint_references/{}".format(id), json=req, scope=SCOPE_SC - ) + resp = scd_session.put(f"/constraint_references/{id}", json=req, scope=SCOPE_SC) assert resp.status_code == 403, resp.content - resp = scd_session.put( - "/constraint_references/{}".format(id), json=req, scope=SCOPE_CP - ) + resp = scd_session.put(f"/constraint_references/{id}", json=req, scope=SCOPE_CP) assert resp.status_code == 403, resp.content - resp = scd_session.put( - "/constraint_references/{}".format(id), json=req, scope=SCOPE_CM - ) + resp = scd_session.put(f"/constraint_references/{id}", json=req, scope=SCOPE_CM) assert resp.status_code == 201, resp.content data = resp.json() @@ -172,7 +160,7 @@ def test_get_constraint_by_id(ids, scd_api, scd_session): auths = (SCOPE_CM, SCOPE_CP) for scope in auths: - resp = scd_session.get("/constraint_references/{}".format(id), scope=scope) + resp = scd_session.get(f"/constraint_references/{id}", scope=scope) assert resp.status_code == 200, resp.content data = resp.json() @@ -293,7 +281,7 @@ def test_get_constraint_by_search_latest_time_excluded(ids, scd_api, scd_session def test_mutate_constraint(ids, scd_api, scd_session): id = ids(CONSTRAINT_TYPE) # GET current constraint - resp = scd_session.get("/constraint_references/{}".format(id), scope=SCOPE_CP) + resp = scd_session.get(f"/constraint_references/{id}", scope=SCOPE_CP) assert resp.status_code == 200, resp.content existing_constraint = resp.json().get("constraint_reference", None) assert existing_constraint is not None @@ -309,29 +297,29 @@ def test_mutate_constraint(ids, scd_api, scd_session): ovn = existing_constraint["ovn"] resp = scd_session.put( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_SC + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_SC ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.put( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CP + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CP ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.put( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CM_SA + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CM_SA ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.put( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_AA + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_AA ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.put( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CM + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CM ) - assert resp.status_code == 200, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 200, f"ovn:{ovn}\nresponse: {resp.content}" data = resp.json() constraint = data["constraint_reference"] @@ -346,7 +334,7 @@ def test_mutate_constraint(ids, scd_api, scd_session): def test_delete_constraint(ids, scd_api, scd_session): id = ids(CONSTRAINT_TYPE) - resp = scd_session.get("/constraint_references/{}".format(id), scope=SCOPE_CP) + resp = scd_session.get(f"/constraint_references/{id}", scope=SCOPE_CP) assert resp.status_code == 200, resp.content existing_constraint = resp.json().get("constraint_reference", None) assert existing_constraint is not None @@ -362,36 +350,36 @@ def test_delete_constraint(ids, scd_api, scd_session): ovn = existing_constraint["ovn"] resp = scd_session.delete( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_SC + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_SC ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.delete( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CP + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CP ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.delete( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CM_SA + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CM_SA ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.delete( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_AA + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_AA ) - assert resp.status_code == 403, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 403, f"ovn:{ovn}\nresponse: {resp.content}" resp = scd_session.delete( - "/constraint_references/{}/{}".format(id, ovn), json=req, scope=SCOPE_CM + f"/constraint_references/{id}/{ovn}", json=req, scope=SCOPE_CM ) - assert resp.status_code == 200, "ovn:{}\nresponse: {}".format(ovn, resp.content) + assert resp.status_code == 200, f"ovn:{ovn}\nresponse: {resp.content}" @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_CM) @depends_on(test_delete_constraint) def test_get_deleted_constraint_by_id(ids, scd_api, scd_session): - resp = scd_session.get("/constraint_references/{}".format(ids(CONSTRAINT_TYPE))) + resp = scd_session.get(f"/constraint_references/{ids(CONSTRAINT_TYPE)}") assert resp.status_code == 404, resp.content diff --git a/monitoring/prober/scd/test_constraints_with_subscriptions.py b/monitoring/prober/scd/test_constraints_with_subscriptions.py index a01366d3d2..33a5e359db 100644 --- a/monitoring/prober/scd/test_constraints_with_subscriptions.py +++ b/monitoring/prober/scd/test_constraints_with_subscriptions.py @@ -9,7 +9,6 @@ """ import datetime -from typing import Dict from monitoring.monitorlib import scd from monitoring.monitorlib.geo import Circle @@ -50,7 +49,7 @@ def _make_c1_request(): } -def _make_sub_req(base_url: str, notify_ops: bool, notify_constraints: bool) -> Dict: +def _make_sub_req(base_url: str, notify_ops: bool, notify_constraints: bool) -> dict: time_start = datetime.datetime.now(datetime.UTC) time_end = time_start + datetime.timedelta(minutes=60) return { @@ -67,9 +66,9 @@ def _make_sub_req(base_url: str, notify_ops: bool, notify_constraints: bool) -> def _read_both_scope(scd_api: str) -> str: if scd_api == scd.API_0_3_17: - return "{} {}".format(SCOPE_SC.value, SCOPE_CP.value) + return f"{SCOPE_SC.value} {SCOPE_CP.value}" else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") def _read_ops_scope(scd_api: str) -> str: @@ -83,7 +82,7 @@ def _read_constraints_scope(scd_api: str) -> str: if scd_api == scd.API_0_3_17: return SCOPE_CP else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") @for_api_versions(scd.API_0_3_17) @@ -105,11 +104,11 @@ def test_subs_do_not_exist(ids, scd_api, scd_session, scd_session2): sub_scope = _read_subs_scope(scd_api) - resp = scd_session.get("/subscriptions/{}".format(ids(SUB1_TYPE)), scope=sub_scope) + resp = scd_session.get(f"/subscriptions/{ids(SUB1_TYPE)}", scope=sub_scope) assert resp.status_code == 404, resp.content - resp = scd_session.get("/subscriptions/{}".format(ids(SUB2_TYPE)), scope=sub_scope) + resp = scd_session.get(f"/subscriptions/{ids(SUB2_TYPE)}", scope=sub_scope) assert resp.status_code == 404, resp.content - resp = scd_session.get("/subscriptions/{}".format(ids(SUB3_TYPE)), scope=sub_scope) + resp = scd_session.get(f"/subscriptions/{ids(SUB3_TYPE)}", scope=sub_scope) assert resp.status_code == 404, resp.content @@ -122,7 +121,7 @@ def test_create_subs(ids, scd_api, scd_session, scd_session2): req = _make_sub_req(SUB_BASE_URL_A, notify_ops=True, notify_constraints=False) resp = scd_session2.put( - "/subscriptions/{}".format(ids(SUB1_TYPE)), + f"/subscriptions/{ids(SUB1_TYPE)}", json=req, scope=_read_ops_scope(scd_api), ) @@ -130,7 +129,7 @@ def test_create_subs(ids, scd_api, scd_session, scd_session2): req = _make_sub_req(SUB_BASE_URL_B, notify_ops=False, notify_constraints=True) resp = scd_session2.put( - "/subscriptions/{}".format(ids(SUB2_TYPE)), + f"/subscriptions/{ids(SUB2_TYPE)}", json=req, scope=_read_constraints_scope(scd_api), ) @@ -138,7 +137,7 @@ def test_create_subs(ids, scd_api, scd_session, scd_session2): req = _make_sub_req(SUB_BASE_URL_B, notify_ops=True, notify_constraints=True) resp = scd_session2.put( - "/subscriptions/{}".format(ids(SUB3_TYPE)), + f"/subscriptions/{ids(SUB3_TYPE)}", json=req, scope=_read_both_scope(scd_api), ) @@ -150,7 +149,7 @@ def test_create_subs(ids, scd_api, scd_session, scd_session2): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_CM) def test_constraint_does_not_exist(ids, scd_api, scd_session, scd_session2): - resp = scd_session.get("/constraint_references/{}".format(ids(CONSTRAINT_TYPE))) + resp = scd_session.get(f"/constraint_references/{ids(CONSTRAINT_TYPE)}") assert resp.status_code == 404, resp.content @@ -160,9 +159,7 @@ def test_constraint_does_not_exist(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_CM) def test_create_constraint(ids, scd_api, scd_session, scd_session2): req = _make_c1_request() - resp = scd_session.put( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), json=req - ) + resp = scd_session.put(f"/constraint_references/{ids(CONSTRAINT_TYPE)}", json=req) assert resp.status_code == 201, resp.content data = resp.json() @@ -206,7 +203,7 @@ def test_create_constraint(ids, scd_api, scd_session, scd_session2): def test_mutate_constraint(ids, scd_api, scd_session, scd_session2): # GET current constraint resp = scd_session.get( - "/constraint_references/{}".format(ids(CONSTRAINT_TYPE)), + f"/constraint_references/{ids(CONSTRAINT_TYPE)}", scope=_read_constraints_scope(scd_api), ) assert resp.status_code == 200, resp.content @@ -230,7 +227,7 @@ def test_mutate_constraint(ids, scd_api, scd_session, scd_session2): scope=SCOPE_CM, ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content data = resp.json() @@ -271,7 +268,7 @@ def test_mutate_constraint(ids, scd_api, scd_session, scd_session2): def test_mutate_subs(ids, scd_api, scd_session2, scd_session): # GET current sub1 before mutation resp = scd_session2.get( - "/subscriptions/{}".format(ids(SUB1_TYPE)), scope=_read_subs_scope(scd_api) + f"/subscriptions/{ids(SUB1_TYPE)}", scope=_read_subs_scope(scd_api) ) assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) @@ -287,7 +284,7 @@ def test_mutate_subs(ids, scd_api, scd_session2, scd_session): ) key = "constraint_references" else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content data = resp.json() @@ -297,7 +294,7 @@ def test_mutate_subs(ids, scd_api, scd_session2, scd_session): # GET current sub3 before mutation resp = scd_session2.get( - "/subscriptions/{}".format(ids(SUB3_TYPE)), scope=_read_subs_scope(scd_api) + f"/subscriptions/{ids(SUB3_TYPE)}", scope=_read_subs_scope(scd_api) ) assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) @@ -313,14 +310,14 @@ def test_mutate_subs(ids, scd_api, scd_session2, scd_session): scope=_read_both_scope(scd_api), ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content data = resp.json() if scd_api == scd.API_0_3_17: assert not data.get("constraint_references", []), data else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") # Preconditions: @@ -333,7 +330,7 @@ def test_mutate_subs(ids, scd_api, scd_session2, scd_session): @default_scope(SCOPE_CM) def test_mutate_constraint2(ids, scd_api, scd_session, scd_session2): # GET current constraint - resp = scd_session.get("/constraint_references/{}".format(ids(CONSTRAINT_TYPE))) + resp = scd_session.get(f"/constraint_references/{ids(CONSTRAINT_TYPE)}") assert resp.status_code == 200, resp.content existing_constraint = resp.json().get("constraint_reference", None) assert existing_constraint is not None @@ -355,7 +352,7 @@ def test_mutate_constraint2(ids, scd_api, scd_session, scd_session2): scope=SCOPE_CM, ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content data = resp.json() @@ -408,7 +405,7 @@ def test_mutate_constraint2(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_CM) def test_delete_constraint(ids, scd_api, scd_session, scd_session2): if scd_api == scd.API_0_3_17: - resp = scd_session.get("/constraint_references/{}".format(ids(CONSTRAINT_TYPE))) + resp = scd_session.get(f"/constraint_references/{ids(CONSTRAINT_TYPE)}") assert resp.status_code == 200, resp.content existing_constraint = resp.json().get("constraint_reference", None) resp = scd_session.delete( @@ -417,7 +414,7 @@ def test_delete_constraint(ids, scd_api, scd_session, scd_session2): ) ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content @@ -430,7 +427,7 @@ def test_delete_subs(ids, scd_api, scd_session2, scd_session): for sub_id in (ids(SUB1_TYPE), ids(SUB2_TYPE), ids(SUB3_TYPE)): if scd_api == scd.API_0_3_17: resp = scd_session2.get( - "/subscriptions/{}".format(sub_id), scope=_read_both_scope(scd_api) + f"/subscriptions/{sub_id}", scope=_read_both_scope(scd_api) ) assert resp.status_code == 200, resp.content sub = resp.json().get("subscription", None) @@ -439,7 +436,7 @@ def test_delete_subs(ids, scd_api, scd_session2, scd_session): scope=_read_both_scope(scd_api), ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content diff --git a/monitoring/prober/scd/test_operation_references_error_cases.py b/monitoring/prober/scd/test_operation_references_error_cases.py index 7e96099b3d..ad4d6bef9a 100644 --- a/monitoring/prober/scd/test_operation_references_error_cases.py +++ b/monitoring/prober/scd/test_operation_references_error_cases.py @@ -26,7 +26,7 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_ref_area_too_large(scd_api, scd_session): - with open("./scd/resources/op_ref_area_too_large.json", "r") as f: + with open("./scd/resources/op_ref_area_too_large.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) assert resp.status_code == 400, resp.content @@ -35,7 +35,7 @@ def test_op_ref_area_too_large(scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_ref_start_end_times_past(scd_api, scd_session): - with open("./scd/resources/op_ref_start_end_times_past.json", "r") as f: + with open("./scd/resources/op_ref_start_end_times_past.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) # It is ok (and useful) to query for past Operations that may not yet have @@ -48,7 +48,7 @@ def test_op_ref_start_end_times_past(scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_ref_incorrect_units(scd_api, scd_session): - with open("./scd/resources/op_ref_incorrect_units.json", "r") as f: + with open("./scd/resources/op_ref_incorrect_units.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) assert resp.status_code == 400, resp.content @@ -57,7 +57,7 @@ def test_op_ref_incorrect_units(scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_ref_incorrect_altitude_ref(scd_api, scd_session): - with open("./scd/resources/op_ref_incorrect_altitude_ref.json", "r") as f: + with open("./scd/resources/op_ref_incorrect_altitude_ref.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) assert resp.status_code == 400, resp.content @@ -66,160 +66,130 @@ def test_op_ref_incorrect_altitude_ref(scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_uss_base_url_non_tls(ids, scd_api, scd_session): - with open("./scd/resources/op_uss_base_url_non_tls.json", "r") as f: + with open("./scd/resources/op_uss_base_url_non_tls.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_subscription_id(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_subscription.json", "r") as f: + with open("./scd/resources/op_bad_subscription.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_subscription_id_random(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_subscription.json", "r") as f: + with open("./scd/resources/op_bad_subscription.json") as f: req = json.load(f) req["subscription_id"] = uuid.uuid4().hex - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_new_and_existing_subscription(ids, scd_api, scd_session): - with open("./scd/resources/op_new_and_existing_subscription.json", "r") as f: + with open("./scd/resources/op_new_and_existing_subscription.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_end_time_past(ids, scd_api, scd_session): - with open("./scd/resources/op_end_time_past.json", "r") as f: + with open("./scd/resources/op_end_time_past.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_already_exists(ids, scd_api, scd_session): - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 201, resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 409, resp.content # Delete operation - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}") assert resp.status_code == 200, resp.content # Verify deletion - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 404, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_400_version1(ids, scd_api, scd_session): - with open("./scd/resources/op_400_version1.json", "r") as f: + with open("./scd/resources/op_400_version1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_state_version0(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_state_version0.json", "r") as f: + with open("./scd/resources/op_bad_state_version0.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_lat_lon_range(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_lat_lon_range.json", "r") as f: + with open("./scd/resources/op_bad_lat_lon_range.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_area_too_large_put(ids, scd_api, scd_session): - with open("./scd/resources/op_area_too_large_put.json", "r") as f: + with open("./scd/resources/op_area_too_large_put.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_time_format(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_time_format.json", "r") as f: + with open("./scd/resources/op_bad_time_format.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_volume(ids, scd_api, scd_session): - with open("./scd/resources/op_bad_volume.json", "r") as f: + with open("./scd/resources/op_bad_volume.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_repeated_requests(ids, scd_api, scd_session): - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 201, resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] @@ -228,24 +198,20 @@ def test_op_repeated_requests(ids, scd_api, scd_session): assert "ovn" in resp.json()["operational_intent_reference"], resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 409, resp.content # Delete operation - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}") assert resp.status_code == 200, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_invalid_id(scd_api, scd_session): - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) resp = scd_session.put("/operational_intent_references/not_uuid_format", json=req) assert resp.status_code == 400, resp.content @@ -255,31 +221,27 @@ def test_op_invalid_id(scd_api, scd_session): @default_scope(SCOPE_SC) def test_missing_conflicted_operation(ids, scd_api, scd_session): # Emplace the initial version of Operation 1 - with open("./scd/resources/op_missing_initial.yaml", "r") as f: + with open("./scd/resources/op_missing_initial.yaml") as f: req = yaml.full_load(f) extents = Volume4DCollection.from_f3548v21(req["extents"]) dt = arrow.utcnow().datetime - extents.time_start.datetime req["extents"] = extents.offset_times(dt).to_f3548v21() - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 201, resp.content ovn1a = resp.json()["operational_intent_reference"]["ovn"] sub_id = resp.json()["operational_intent_reference"]["subscription_id"] # Emplace the pre-existing Operation that conflicted in the original observation - with open("./scd/resources/op_missing_preexisting_unknown.yaml", "r") as f: + with open("./scd/resources/op_missing_preexisting_unknown.yaml") as f: req = yaml.full_load(f) extents = Volume4DCollection.from_f3548v21(req["extents"]) req["extents"] = extents.offset_times(dt).to_f3548v21() req["key"] = [ovn1a] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE2)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE2)}", json=req) assert resp.status_code == 201, resp.content # Attempt to update Operation 1 without OVN for the pre-existing Operation - with open("./scd/resources/op_missing_update.json", "r") as f: + with open("./scd/resources/op_missing_update.json") as f: req = json.load(f) req["extents"] = ( Volume4DCollection.from_f3548v21(req["extents"]).offset_times(dt).to_f3548v21() @@ -287,7 +249,7 @@ def test_missing_conflicted_operation(ids, scd_api, scd_session): req["key"] = [ovn1a] req["subscription_id"] = sub_id resp = scd_session.put( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn1a), json=req + f"/operational_intent_references/{ids(OP_TYPE)}/{ovn1a}", json=req ) assert resp.status_code == 409, resp.content @@ -299,7 +261,7 @@ def test_missing_conflicted_operation(ids, scd_api, scd_session): ], resp.content # Perform an area-based query on the area occupied by Operation 1 - with open("./scd/resources/op_missing_query.json", "r") as f: + with open("./scd/resources/op_missing_query.json") as f: req = json.load(f) req["area_of_interest"] = ( Volume4D.from_f3548v21(req["area_of_interest"]).offset_time(dt).to_f3548v21() @@ -317,7 +279,7 @@ def test_missing_conflicted_operation(ids, scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_big_operation_search(scd_api, scd_session): - with open("./scd/resources/op_big_operation.json", "r") as f: + with open("./scd/resources/op_big_operation.json") as f: req = json.load(f) aoi = Volume4D.from_f3548v21(req["area_of_interest"]) dt = arrow.utcnow().datetime - aoi.time_start.datetime @@ -331,7 +293,7 @@ def test_big_operation_search(scd_api, scd_session): def test_clean_up(ids, scd_api, scd_session): for op_id in (ids(OP_TYPE), ids(OP_TYPE2)): resp = scd_session.get( - "/operational_intent_references/{}".format(op_id), scope=SCOPE_SC + f"/operational_intent_references/{op_id}", scope=SCOPE_SC ) if resp.status_code == 200: # only the owner of the subscription can delete a operation reference. @@ -339,7 +301,7 @@ def test_clean_up(ids, scd_api, scd_session): assert "ovn" in resp.json()["operational_intent_reference"], resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(op_id, ovn), + f"/operational_intent_references/{op_id}/{ovn}", scope=SCOPE_SC, ) assert resp.status_code == 200, resp.content diff --git a/monitoring/prober/scd/test_operation_references_state_transition.py b/monitoring/prober/scd/test_operation_references_state_transition.py index faec8826e8..3835702189 100644 --- a/monitoring/prober/scd/test_operation_references_state_transition.py +++ b/monitoring/prober/scd/test_operation_references_state_transition.py @@ -20,11 +20,9 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_op_accepted(ids, scd_api, scd_session): # Accepted for the first time - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 201, resp.content @@ -32,13 +30,13 @@ def test_op_accepted(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_op_activated(ids, scd_api, scd_session): # GET current op - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None # Accepted to Activated - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) req["state"] = "Activated" req["old_version"] = 1 @@ -54,40 +52,34 @@ def test_op_activated(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_op_accepted_bad1(ids, scd_api, scd_session): # GET current op - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None # Activated to Accepted with bad version number 0 - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) req["key"] = [existing_op["ovn"]] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 409, resp.content @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_bad_state_transition(ids, scd_api, scd_session): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 200, resp.content ovn = resp.json().get("operational_intent_reference", {}).get("ovn", None) # Delete operation - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}") assert resp.status_code == 200, resp.content # Create operation with Closed state - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) req["state"] = "Ended" - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content diff --git a/monitoring/prober/scd/test_operation_simple.py b/monitoring/prober/scd/test_operation_simple.py index 0f8e93dd53..8bf6da2eaa 100644 --- a/monitoring/prober/scd/test_operation_simple.py +++ b/monitoring/prober/scd/test_operation_simple.py @@ -51,7 +51,7 @@ def _make_op1_request(): @default_scope(SCOPE_SC) @depends_on(test_ensure_clean_workspace) def test_op_does_not_exist_get(ids, scd_api, scd_session): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 404, resp.content @@ -106,9 +106,7 @@ def test_op_does_not_exist_query( def test_create_op_single_extent(ids, scd_api, scd_session): req = _make_op1_request() req["extents"] = req["extents"][0] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -117,9 +115,7 @@ def test_create_op_single_extent(ids, scd_api, scd_session): def test_create_op_missing_time_start(ids, scd_api, scd_session): req = _make_op1_request() del req["extents"][0]["time_start"] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -128,9 +124,7 @@ def test_create_op_missing_time_start(ids, scd_api, scd_session): def test_create_op_missing_time_end(ids, scd_api, scd_session): req = _make_op1_request() del req["extents"][0]["time_end"] - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -140,7 +134,7 @@ def test_create_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): if scd_session_cp: resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), + f"/operational_intent_references/{ids(OP_TYPE)}", json=req, scope=SCOPE_CP, ) @@ -148,14 +142,14 @@ def test_create_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): if scd_session_cm: resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), + f"/operational_intent_references/{ids(OP_TYPE)}", json=req, scope=SCOPE_CM, ) assert resp.status_code == 403, resp.content resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), + f"/operational_intent_references/{ids(OP_TYPE)}", json=req, scope=SCOPE_SC, ) @@ -181,18 +175,18 @@ def test_create_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): def test_get_op_by_id(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): if scd_session_cp: resp = scd_session.get( - "/operational_intent_references/{}".format(ids(OP_TYPE)), scope=SCOPE_CP + f"/operational_intent_references/{ids(OP_TYPE)}", scope=SCOPE_CP ) assert resp.status_code == 403, resp.content if scd_session_cm: resp = scd_session.get( - "/operational_intent_references/{}".format(ids(OP_TYPE)), scope=SCOPE_CM + f"/operational_intent_references/{ids(OP_TYPE)}", scope=SCOPE_CM ) assert resp.status_code == 403, resp.content resp = scd_session.get( - "/operational_intent_references/{}".format(ids(OP_TYPE)), scope=SCOPE_SC + f"/operational_intent_references/{ids(OP_TYPE)}", scope=SCOPE_SC ) assert resp.status_code == 200, resp.content @@ -290,7 +284,7 @@ def test_get_op_by_search_latest_time_included(ids, scd_api, scd_session): @depends_on(test_create_op) def test_get_op_by_id_other_uss(ids, scd_session2): resp = scd_session2.get( - "/operational_intent_references/{}".format(ids(OP_TYPE)), scope=SCOPE_SC + f"/operational_intent_references/{ids(OP_TYPE)}", scope=SCOPE_SC ) assert resp.status_code == 200, resp.content @@ -360,7 +354,7 @@ def test_get_op_by_search_latest_time_excluded(ids, scd_api, scd_session): @depends_on(test_create_op) def test_mutate_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): # GET current op - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None, resp.json() @@ -377,7 +371,7 @@ def test_mutate_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): if scd_session_cp: resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), + f"/operational_intent_references/{ids(OP_TYPE)}", json=req, scope=SCOPE_CP, ) @@ -385,7 +379,7 @@ def test_mutate_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): if scd_session_cm: resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP_TYPE)), + f"/operational_intent_references/{ids(OP_TYPE)}", json=req, scope=SCOPE_CM, ) @@ -410,27 +404,27 @@ def test_mutate_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): @depends_on(test_mutate_op) def test_delete_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): resp = scd_session.get( - "/operational_intent_references/{}".format(ids(OP_TYPE)), scope=SCOPE_SC + f"/operational_intent_references/{ids(OP_TYPE)}", scope=SCOPE_SC ) assert resp.status_code == 200, resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] if scd_session_cp: resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn), + f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}", scope=SCOPE_CP, ) assert resp.status_code == 403, resp.content if scd_session_cm: resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn), + f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}", scope=SCOPE_CM, ) assert resp.status_code == 403, resp.content resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn), scope=SCOPE_SC + f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}", scope=SCOPE_SC ) assert resp.status_code == 200, resp.content @@ -438,7 +432,7 @@ def test_delete_op(ids, scd_api, scd_session, scd_session_cp, scd_session_cm): @default_scope(SCOPE_SC) @depends_on(test_delete_op) def test_get_deleted_op_by_id(ids, scd_api, scd_session): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 404, resp.content diff --git a/monitoring/prober/scd/test_operation_simple_heavy_traffic.py b/monitoring/prober/scd/test_operation_simple_heavy_traffic.py index ce9e52c08f..ebc53a8ea2 100644 --- a/monitoring/prober/scd/test_operation_simple_heavy_traffic.py +++ b/monitoring/prober/scd/test_operation_simple_heavy_traffic.py @@ -22,7 +22,7 @@ BASE_URL = make_fake_url() OP_TYPES = [ - register_resource_type(10 + i, "Operational intent {}".format(i)) for i in range(20) + register_resource_type(10 + i, f"Operational intent {i}") for i in range(20) ] ovn_map = {} @@ -61,7 +61,7 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_ops_do_not_exist_get(ids, scd_api, scd_session): for op_id in map(ids, OP_TYPES): - resp = scd_session.get("/operation_references/{}".format(op_id)) + resp = scd_session.get(f"/operation_references/{op_id}") assert resp.status_code == 404, resp.content @@ -94,7 +94,7 @@ def test_create_ops(ids, scd_api, scd_session): req["key"] = list(ovn_map.values()) resp = scd_session.put( - "/operational_intent_references/{}".format(op_id), json=req, scope=SCOPE_SC + f"/operational_intent_references/{op_id}", json=req, scope=SCOPE_SC ) assert resp.status_code == 201, resp.content @@ -122,7 +122,7 @@ def test_create_ops(ids, scd_api, scd_session): def test_get_ops_by_ids(ids, scd_api, scd_session): for op_id in map(ids, OP_TYPES): resp = scd_session.get( - "/operational_intent_references/{}".format(op_id), scope=SCOPE_SC + f"/operational_intent_references/{op_id}", scope=SCOPE_SC ) assert resp.status_code == 200, resp.content @@ -231,7 +231,7 @@ def test_get_ops_by_search_latest_time_excluded(ids, scd_api, scd_session): def test_mutate_ops(ids, scd_api, scd_session): for idx, op_id in enumerate(map(ids, OP_TYPES)): # GET current op - resp = scd_session.get("/operational_intent_references/{}".format(op_id)) + resp = scd_session.get(f"/operational_intent_references/{op_id}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None @@ -282,7 +282,7 @@ def test_mutate_ops(ids, scd_api, scd_session): def test_delete_op(ids, scd_api, scd_session): for op_id in map(ids, OP_TYPES): resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(op_id, ovn_map[op_id]) + f"/operational_intent_references/{op_id}/{ovn_map[op_id]}" ) assert resp.status_code == 200, resp.content @@ -291,7 +291,7 @@ def test_delete_op(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_get_deleted_ops_by_ids(ids, scd_api, scd_session): for op_id in map(ids, OP_TYPES): - resp = scd_session.get("/operational_intent_references/{}".format(op_id)) + resp = scd_session.get(f"/operational_intent_references/{op_id}") assert resp.status_code == 404, resp.content diff --git a/monitoring/prober/scd/test_operation_simple_heavy_traffic_concurrent.py b/monitoring/prober/scd/test_operation_simple_heavy_traffic_concurrent.py index c7c93e449b..e6d4f702f2 100644 --- a/monitoring/prober/scd/test_operation_simple_heavy_traffic_concurrent.py +++ b/monitoring/prober/scd/test_operation_simple_heavy_traffic_concurrent.py @@ -34,8 +34,7 @@ BASE_URL = make_fake_url() # TODO(#742): Increase number of concurrent operations from 20 to 100 OP_TYPES = [ - register_resource_type(110 + i, "Operational intent {}".format(i)) - for i in range(20) + register_resource_type(110 + i, f"Operational intent {i}") for i in range(20) ] GROUP_SIZE = len(OP_TYPES) // 3 + (1 if len(OP_TYPES) % 3 > 0 else 0) # Semaphore is added to limit the number of simultaneous requests, @@ -150,15 +149,13 @@ async def _put_operation_async( async with SEMAPHORE: if scd_api == scd.API_0_3_17: if create_new: - req_url = "/operational_intent_references/{}".format(op_id) + req_url = f"/operational_intent_references/{op_id}" result = await scd_session_async.put(req_url, data=req), req_url, req else: - req_url = "/operational_intent_references/{}/{}".format( - op_id, ovn_map[op_id] - ) + req_url = f"/operational_intent_references/{op_id}/{ovn_map[op_id]}" result = await scd_session_async.put(req_url, data=req), req_url, req else: - raise ValueError("Unsupported SCD API version: {}".format(scd_api)) + raise ValueError(f"Unsupported SCD API version: {scd_api}") return result @@ -166,10 +163,10 @@ async def _get_operation_async(op_id, scd_session_async, scd_api): async with SEMAPHORE: if scd_api == scd.API_0_3_17: result = await scd_session_async.get( - "/operational_intent_references/{}".format(op_id), scope=SCOPE_SC + f"/operational_intent_references/{op_id}", scope=SCOPE_SC ) else: - raise ValueError("Unsupported SCD API version: {}".format(scd_api)) + raise ValueError(f"Unsupported SCD API version: {scd_api}") return result @@ -186,20 +183,20 @@ async def _query_operation_async(idx, scd_session_async, scd_api): "/operational_intent_references/query", json=req_json, scope=SCOPE_SC ) else: - raise ValueError("Unsupported SCD API version: {}".format(scd_api)) + raise ValueError(f"Unsupported SCD API version: {scd_api}") return result def _build_mutate_request(idx, op_id, op_map, scd_session, scd_api): # GET current op if scd_api == scd.API_0_3_17: - resp = scd_session.get("/operational_intent_references/{}".format(op_id)) + resp = scd_session.get(f"/operational_intent_references/{op_id}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None op_map[op_id] = existing_op else: - raise ValueError("Unsupported SCD API version: {}".format(scd_api)) + raise ValueError(f"Unsupported SCD API version: {scd_api}") # mutate requests should be constructed at a good time gap from the create requests. additional_time_gap = idx * 10 @@ -218,11 +215,11 @@ def _build_mutate_request(idx, op_id, op_map, scd_session, scd_api): async def _delete_operation_async(op_id, scd_session_async, scd_api): if scd_api == scd.API_0_3_17: result = await scd_session_async.delete( - "/operational_intent_references/{}/{}".format(op_id, ovn_map[op_id]), + f"/operational_intent_references/{op_id}/{ovn_map[op_id]}", scope=SCOPE_SC, ) else: - raise ValueError("Unsupported SCD API version: {}".format(scd_api)) + raise ValueError(f"Unsupported SCD API version: {scd_api}") return result @@ -441,11 +438,9 @@ def test_mutate_ops_concurrent(ids, scd_api, scd_session, scd_session_async): op_id for op_id, resp in op_resp_map.items() if resp["status_code"] != 200 ] if ops_with_bad_status: - msg = "{} operational intents failed to mutate:\n".format( - len(ops_with_bad_status) - ) + msg = f"{len(ops_with_bad_status)} operational intents failed to mutate:\n" msg += "\n".join( - "{}: {}".format(op_id, op_resp_map[op_id]) for op_id in ops_with_bad_status + f"{op_id}: {op_resp_map[op_id]}" for op_id in ops_with_bad_status ) assert False, msg diff --git a/monitoring/prober/scd/test_operation_special_cases.py b/monitoring/prober/scd/test_operation_special_cases.py index 084c15d216..ba4ccb79b1 100644 --- a/monitoring/prober/scd/test_operation_special_cases.py +++ b/monitoring/prober/scd/test_operation_special_cases.py @@ -35,20 +35,16 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_request_1(ids, scd_api, scd_session): - with open("./scd/resources/op_request_1.json", "r") as f: + with open("./scd/resources/op_request_1.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP1_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP1_TYPE)}", json=req) assert resp.status_code == 201, resp.content data = resp.json() assert "operational_intent_reference" in data, data assert "ovn" in resp.json()["operational_intent_reference"], data ovn = data["operational_intent_reference"]["ovn"] - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{ids(OP1_TYPE)}/{ovn}") assert resp.status_code == 200, resp.content @@ -57,11 +53,9 @@ def test_op_request_1(ids, scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_request_2(ids, scd_api, scd_session): - with open("./scd/resources/op_request_2.json", "r") as f: + with open("./scd/resources/op_request_2.json") as f: req = json.load(f) - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP2_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP2_TYPE)}", json=req) assert resp.status_code == 400, resp.content @@ -70,7 +64,7 @@ def test_op_request_2(ids, scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_query_degenerate_polygon(scd_api, scd_session): - with open("./scd/resources/op_request_3.json", "r") as f: + with open("./scd/resources/op_request_3.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) assert resp.status_code == 400, resp.content @@ -81,7 +75,7 @@ def test_op_query_degenerate_polygon(scd_api, scd_session): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op_query_not_area_too_large(scd_api, scd_session): - with open("./scd/resources/op_request_4.json", "r") as f: + with open("./scd/resources/op_request_4.json") as f: req = json.load(f) resp = scd_session.post("/operational_intent_references/query", json=req) assert resp.status_code == 200, resp.content @@ -121,7 +115,7 @@ def test_id_conversion_bug(ids, scd_api, scd_session): "uss_base_url": make_fake_url("uss/v1/"), "notify_for_constraints": True, } - resp = scd_session.put("/subscriptions/{}".format(sub_uuid), json=req) + resp = scd_session.put(f"/subscriptions/{sub_uuid}", json=req) assert resp.status_code == 200, resp.content req["extents"]["time_start"]["value"] = ( @@ -135,7 +129,7 @@ def test_id_conversion_bug(ids, scd_api, scd_session): json=req, ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content if scd_api == scd.API_0_3_17: @@ -145,7 +139,7 @@ def test_id_conversion_bug(ids, scd_api, scd_session): ) ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content diff --git a/monitoring/prober/scd/test_operations_simple.py b/monitoring/prober/scd/test_operations_simple.py index e89f96f2d8..ef425c79b8 100644 --- a/monitoring/prober/scd/test_operations_simple.py +++ b/monitoring/prober/scd/test_operations_simple.py @@ -10,7 +10,6 @@ """ import datetime -from typing import Dict, Tuple from monitoring.monitorlib import scd from monitoring.monitorlib.geo import Altitude, Circle @@ -69,7 +68,7 @@ def _make_op2_request(): # Parses `subscribers` response field into Dict[USS base URL, Dict[Subscription ID, Notification index]] -def _parse_subscribers(subscribers: Dict) -> Dict[str, Dict[str, int]]: +def _parse_subscribers(subscribers: dict) -> dict[str, dict[str, int]]: return { to_notify["uss_base_url"]: { sub["subscription_id"]: sub["notification_index"] @@ -81,7 +80,7 @@ def _parse_subscribers(subscribers: Dict) -> Dict[str, Dict[str, int]]: # Parses AirspaceConflictResponse entities into Dict[Operation ID, Operation Reference] + # Dict[Constraint ID, Constraint Reference] -def _parse_conflicts(conflicts: Dict) -> Tuple[Dict[str, Dict], Dict[str, Dict], set]: +def _parse_conflicts(conflicts: dict) -> tuple[dict[str, dict], dict[str, dict], set]: missing_operational_intents = conflicts.get("missing_operational_intents", []) ops = {op["id"]: op for op in missing_operational_intents} missing_constraints = conflicts.get("missing_constraints", []) @@ -107,7 +106,7 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session, scd_session2): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op1_does_not_exist_get_1(ids, scd_api, scd_session, scd_session2): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 404, resp.content @@ -117,7 +116,7 @@ def test_op1_does_not_exist_get_1(ids, scd_api, scd_session, scd_session2): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_op1_does_not_exist_get_2(ids, scd_api, scd_session2): - resp = scd_session2.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session2.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 404, resp.content @@ -176,9 +175,7 @@ def test_op1_does_not_exist_query_2(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_SC) def test_create_op1(ids, scd_api, scd_session, scd_session2): req = _make_op1_request() - resp = scd_session.put( - "/operational_intent_references/{}".format(ids(OP1_TYPE)), json=req - ) + resp = scd_session.put(f"/operational_intent_references/{ids(OP1_TYPE)}", json=req) assert resp.status_code == 201, resp.content data = resp.json() @@ -213,18 +210,18 @@ def test_create_op1(ids, scd_api, scd_session, scd_session2): def test_delete_implicit_sub(ids, scd_api, scd_session, scd_session2): if scd_session is None: return - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 200, resp.content operational_intent_reference = resp.json()["operational_intent_reference"] implicit_sub_id = operational_intent_reference["subscription_id"] # We need to obtain the implicit subscription's version in order to properly attempt to delete it: - sub_resp = scd_session.get("/subscriptions/{}".format(implicit_sub_id)) + sub_resp = scd_session.get(f"/subscriptions/{implicit_sub_id}") assert sub_resp.status_code == 200, sub_resp.content implicit_sub_version = sub_resp.json()["subscription"]["version"] resp = scd_session.delete( - "/subscriptions/{}/{}".format(implicit_sub_id, implicit_sub_version) + f"/subscriptions/{implicit_sub_id}/{implicit_sub_version}" ) # Expect 400 or 409 while we fix the logic in the DSS. Both this test and the DSS were handling things improperly: # allow both the expected (409) and the technically correct but not-in-concordance-with-the-spec result (400) @@ -238,7 +235,7 @@ def test_delete_implicit_sub(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_SC) def test_delete_op1_by_uss2(ids, scd_api, scd_session, scd_session2): resp = scd_session2.delete( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), op1_ovn) + f"/operational_intent_references/{ids(OP1_TYPE)}/{op1_ovn}" ) assert resp.status_code == 403, resp.content @@ -250,9 +247,7 @@ def test_delete_op1_by_uss2(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_SC) def test_create_op2_no_ovn(ids, scd_api, scd_session, scd_session2): req = _make_op2_request() - resp = scd_session2.put( - "/operational_intent_references/{}".format(ids(OP2_TYPE)), json=req - ) + resp = scd_session2.put(f"/operational_intent_references/{ids(OP2_TYPE)}", json=req) # Accepting both 400 and 409: # - dss v0.11.0-rc1 does not allow OIRs in state ACCEPTED without sub or implicit sub parameters and returns a 400 # - the next DSS release (that also needs to pass these prober tests) does not require the subscription parameters @@ -279,7 +274,7 @@ def test_create_op2sub(ids, scd_api, scd_session, scd_session2): } req.update({"notify_for_operational_intents": True}) - resp = scd_session2.put("/subscriptions/{}".format(ids(SUB2_TYPE)), json=req) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}", json=req) assert resp.status_code == 200, resp.content # The Subscription response should mention Op1, but not include its OVN @@ -291,7 +286,7 @@ def test_create_op2sub(ids, scd_api, scd_session, scd_session2): assert data["subscription"]["notification_index"] == 0 - resp = scd_session2.get("/subscriptions/{}".format(ids(SUB2_TYPE))) + resp = scd_session2.get(f"/subscriptions/{ids(SUB2_TYPE)}") assert resp.status_code == 200, resp.content global sub2_version @@ -308,9 +303,7 @@ def test_create_op2sub(ids, scd_api, scd_session, scd_session2): def test_create_op2_no_key(ids, scd_api, scd_session, scd_session2): req = _make_op2_request() req["subscription_id"] = ids(SUB2_TYPE) - resp = scd_session2.put( - "/operational_intent_references/{}".format(ids(OP2_TYPE)), json=req - ) + resp = scd_session2.put(f"/operational_intent_references/{ids(OP2_TYPE)}", json=req) assert resp.status_code == 409, resp.content data = resp.json() assert "missing_operational_intents" in data, data @@ -329,9 +322,7 @@ def test_create_op2(ids, scd_api, scd_session, scd_session2): req = _make_op2_request() req["subscription_id"] = ids(SUB2_TYPE) req["key"] = [op1_ovn] - resp = scd_session2.put( - "/operational_intent_references/{}".format(ids(OP2_TYPE)), json=req - ) + resp = scd_session2.put(f"/operational_intent_references/{ids(OP2_TYPE)}", json=req) assert resp.status_code == 201, resp.content data = resp.json() @@ -349,7 +340,7 @@ def test_create_op2(ids, scd_api, scd_session, scd_session2): assert op["state"] == "Accepted" assert op.get("ovn", "") - resp = scd_session2.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session2.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 200, resp.content implicit_sub_id = resp.json()["operational_intent_reference"]["subscription_id"] @@ -439,7 +430,7 @@ def test_read_ops_from_uss2(ids, scd_api, scd_session, scd_session2): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_mutate_op1_bad_key(ids, scd_api, scd_session, scd_session2): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None, resp.content @@ -453,7 +444,7 @@ def test_mutate_op1_bad_key(ids, scd_api, scd_session, scd_session2): "subscription_id": existing_op["subscription_id"], } resp = scd_session.put( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), op1_ovn), json=req + f"/operational_intent_references/{ids(OP1_TYPE)}/{op1_ovn}", json=req ) assert resp.status_code == 409, resp.content missing_ops, _, _ = _parse_conflicts(resp.json()) @@ -463,7 +454,7 @@ def test_mutate_op1_bad_key(ids, scd_api, scd_session, scd_session2): req["key"] = [op1_ovn] resp = scd_session.put( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), op1_ovn), json=req + f"/operational_intent_references/{ids(OP1_TYPE)}/{op1_ovn}", json=req ) assert resp.status_code == 409, resp.content missing_ops, _, ovns = _parse_conflicts(resp.json()) @@ -481,7 +472,7 @@ def test_mutate_op1_bad_key(ids, scd_api, scd_session, scd_session2): @for_api_versions(scd.API_0_3_17) @default_scope(SCOPE_SC) def test_mutate_op1(ids, scd_api, scd_session, scd_session2): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP1_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP1_TYPE)}") assert resp.status_code == 200, resp.content existing_op = resp.json().get("operational_intent_reference", None) assert existing_op is not None, resp.content @@ -498,7 +489,7 @@ def test_mutate_op1(ids, scd_api, scd_session, scd_session2): "subscription_id": existing_op["subscription_id"], } resp = scd_session.put( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), op1_ovn), json=req + f"/operational_intent_references/{ids(OP1_TYPE)}/{op1_ovn}", json=req ) assert resp.status_code == 200, resp.content @@ -530,9 +521,7 @@ def test_mutate_op1(ids, scd_api, scd_session, scd_session2): def test_delete_dependent_sub(ids, scd_api, scd_session, scd_session2): if scd_session2 is None: return - resp = scd_session2.delete( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version) - ) + resp = scd_session2.delete(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}") assert resp.status_code == 400, resp.content @@ -563,7 +552,7 @@ def test_mutate_sub2(ids, scd_api, scd_session, scd_session2): req["extents"]["time_end"] = Time(time_end).to_f3548v21() req["notify_for_operational_intents"] = False - resp = scd_session2.put("/subscriptions/{}".format(ids(SUB2_TYPE)), json=req) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}", json=req) assert resp.status_code == 400, resp.content req["notify_for_operational_intents"] = True @@ -572,50 +561,38 @@ def test_mutate_sub2(ids, scd_api, scd_session, scd_session2): req["extents"]["time_start"] = Time( time_now + datetime.timedelta(minutes=5) ).to_f3548v21() - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 400, resp.content req["extents"]["time_start"] = Time(time_start).to_f3548v21() # Attempt mutation with end time that doesn't cover Op2 req["extents"]["time_end"] = Time(time_now).to_f3548v21() - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 400, resp.content req["extents"]["time_end"] = Time(time_end).to_f3548v21() # # Attempt mutation with minimum altitude that doesn't cover Op2 req["extents"]["volume"]["altitude_lower"] = Altitude.w84m(10) - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 400, resp.content req["extents"]["volume"]["altitude_lower"] = Altitude.w84m(0) # Attempt mutation with maximum altitude that doesn't cover Op2 req["extents"]["volume"]["altitude_upper"] = Altitude.w84m(10) - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 400, resp.content req["extents"]["volume"]["altitude_upper"] = Altitude.w84m(200) # # Attempt mutation with outline that doesn't cover Op2 old_lat = req["extents"]["volume"]["outline_circle"]["center"]["lat"] req["extents"]["volume"]["outline_circle"]["center"]["lat"] = 45 - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 400, resp.content req["extents"]["volume"]["outline_circle"]["center"]["lat"] = old_lat # Attempt mutation without notifying for Operations # Perform a valid mutation - resp = scd_session2.put( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version), json=req - ) + resp = scd_session2.put(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}", json=req) assert resp.status_code == 200, resp.content # The Subscription response should mention Op1 and Op2, but not include Op1's OVN @@ -628,7 +605,7 @@ def test_mutate_sub2(ids, scd_api, scd_session, scd_session2): assert data["subscription"]["notification_index"] == 2 # Make sure the Subscription is still retrievable specifically - resp = scd_session2.get("/subscriptions/{}".format(ids(SUB2_TYPE))) + resp = scd_session2.get(f"/subscriptions/{ids(SUB2_TYPE)}") assert resp.status_code == 200, resp.content data = resp.json() sub2_version = data["subscription"]["version"] @@ -643,7 +620,7 @@ def test_mutate_sub2(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_SC) def test_delete_op1(ids, scd_api, scd_session, scd_session2): resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP1_TYPE), op1_ovn) + f"/operational_intent_references/{ids(OP1_TYPE)}/{op1_ovn}" ) assert resp.status_code == 200, resp.content @@ -671,7 +648,7 @@ def test_delete_op1(ids, scd_api, scd_session, scd_session2): @default_scope(SCOPE_SC) def test_delete_op2(ids, scd_api, scd_session, scd_session2): resp = scd_session2.delete( - "/operational_intent_references/{}/{}".format(ids(OP2_TYPE), op2_ovn) + f"/operational_intent_references/{ids(OP2_TYPE)}/{op2_ovn}" ) assert resp.status_code == 200, resp.content @@ -685,7 +662,7 @@ def test_delete_op2(ids, scd_api, scd_session, scd_session2): assert ids(SUB2_TYPE) in subscribers[URL_SUB2], subscribers[URL_SUB2] assert subscribers[URL_SUB2][ids(SUB2_TYPE)] == 4 - resp = scd_session2.get("/subscriptions/{}".format(ids(SUB2_TYPE))) + resp = scd_session2.get(f"/subscriptions/{ids(SUB2_TYPE)}") assert resp.status_code == 200, resp.content @@ -700,9 +677,7 @@ def test_delete_op2(ids, scd_api, scd_session, scd_session2): def test_delete_sub2(ids, scd_api, scd_session2): if scd_session2 is None: return - resp = scd_session2.delete( - "/subscriptions/{}/{}".format(ids(SUB2_TYPE), sub2_version) - ) + resp = scd_session2.delete(f"/subscriptions/{ids(SUB2_TYPE)}/{sub2_version}") assert resp.status_code == 200, resp.content diff --git a/monitoring/prober/scd/test_subscription_queries.py b/monitoring/prober/scd/test_subscription_queries.py index bacd703ff2..63dfb2026a 100644 --- a/monitoring/prober/scd/test_subscription_queries.py +++ b/monitoring/prober/scd/test_subscription_queries.py @@ -86,7 +86,7 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_subs_do_not_exist_get(ids, scd_api, scd_session): for sub_id in (ids(SUB1_TYPE), ids(SUB2_TYPE), ids(SUB3_TYPE)): - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 404, resp.content @@ -115,17 +115,17 @@ def test_subs_do_not_exist_query(ids, scd_api, scd_session): @default_scope(SCOPE_SC) def test_create_subs(ids, scd_api, scd_session): resp = scd_session.put( - "/subscriptions/{}".format(ids(SUB1_TYPE)), json=_make_sub1_req(scd_api) + f"/subscriptions/{ids(SUB1_TYPE)}", json=_make_sub1_req(scd_api) ) assert resp.status_code == 200, resp.content resp = scd_session.put( - "/subscriptions/{}".format(ids(SUB2_TYPE)), json=_make_sub2_req(scd_api) + f"/subscriptions/{ids(SUB2_TYPE)}", json=_make_sub2_req(scd_api) ) assert resp.status_code == 200, resp.content resp = scd_session.put( - "/subscriptions/{}".format(ids(SUB3_TYPE)), json=_make_sub3_req(scd_api) + f"/subscriptions/{ids(SUB3_TYPE)}", json=_make_sub3_req(scd_api) ) assert resp.status_code == 200, resp.content @@ -303,7 +303,7 @@ def test_search_time_footprint(ids, scd_api, scd_session): def test_delete_subs(ids, scd_api, scd_session): for sub_id in (ids(SUB1_TYPE), ids(SUB2_TYPE), ids(SUB3_TYPE)): if scd_api == scd.API_0_3_17: - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200 resp = scd_session.delete( "/subscriptions/{}/{}".format( @@ -311,7 +311,7 @@ def test_delete_subs(ids, scd_api, scd_session): ) ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content diff --git a/monitoring/prober/scd/test_subscription_query_time.py b/monitoring/prober/scd/test_subscription_query_time.py index 8eb37f65f8..96756f4d31 100644 --- a/monitoring/prober/scd/test_subscription_query_time.py +++ b/monitoring/prober/scd/test_subscription_query_time.py @@ -46,5 +46,5 @@ def test_subscription_with_invalid_start_time(ids, scd_api, scd_session): req = _make_sub_req(time_start, time_end, 200, 1000, 500, scd_api) req["extents"]["time_start"]["value"] = "something-invalid" - resp = scd_session.put("/subscriptions/{}".format(ids(SUB_TYPE)), json=req) + resp = scd_session.put(f"/subscriptions/{ids(SUB_TYPE)}", json=req) assert resp.status_code == 400, resp.content diff --git a/monitoring/prober/scd/test_subscription_simple.py b/monitoring/prober/scd/test_subscription_simple.py index df18319e5d..4acfcd5850 100644 --- a/monitoring/prober/scd/test_subscription_simple.py +++ b/monitoring/prober/scd/test_subscription_simple.py @@ -72,7 +72,7 @@ def _check_sub1(data, sub_id, scd_api): def test_sub_does_not_exist_get(ids, scd_api, scd_session): if scd_session is None: return - resp = scd_session.get("/subscriptions/{}".format(ids(SUB_TYPE))) + resp = scd_session.get(f"/subscriptions/{ids(SUB_TYPE)}") assert resp.status_code == 404, resp.content @@ -101,7 +101,7 @@ def test_create_sub(ids, scd_api, scd_session): if scd_session is None: return req = _make_sub1_req(scd_api) - resp = scd_session.put("/subscriptions/{}".format(ids(SUB_TYPE)), json=req) + resp = scd_session.put(f"/subscriptions/{ids(SUB_TYPE)}", json=req) assert resp.status_code == 200, resp.content data = resp.json() @@ -122,7 +122,7 @@ def test_create_sub(ids, scd_api, scd_session): def test_get_sub_by_id(ids, scd_api, scd_session): if scd_session is None: return - resp = scd_session.get("/subscriptions/{}".format(ids(SUB_TYPE))) + resp = scd_session.get(f"/subscriptions/{ids(SUB_TYPE)}") assert resp.status_code == 200, resp.content data = resp.json() @@ -156,7 +156,7 @@ def test_mutate_sub(ids, scd_api, scd_session): return # GET current sub1 before mutation - resp = scd_session.get("/subscriptions/{}".format(ids(SUB_TYPE))) + resp = scd_session.get(f"/subscriptions/{ids(SUB_TYPE)}") assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) assert existing_sub is not None @@ -171,7 +171,7 @@ def test_mutate_sub(ids, scd_api, scd_session): json=req, ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content data = resp.json() @@ -192,7 +192,7 @@ def test_delete_sub(ids, scd_api, scd_session): if scd_session is None: return if scd_api == scd.API_0_3_17: - resp = scd_session.get("/subscriptions/{}".format(ids(SUB_TYPE))) + resp = scd_session.get(f"/subscriptions/{ids(SUB_TYPE)}") assert resp.status_code == 200, resp.content resp = scd_session.delete( "/subscriptions/{}/{}".format( @@ -200,7 +200,7 @@ def test_delete_sub(ids, scd_api, scd_session): ) ) else: - raise NotImplementedError("Unsupported API version {}".format(scd_api)) + raise NotImplementedError(f"Unsupported API version {scd_api}") assert resp.status_code == 200, resp.content @@ -209,7 +209,7 @@ def test_delete_sub(ids, scd_api, scd_session): def test_get_deleted_sub_by_id(ids, scd_api, scd_session): if scd_session is None: return - resp = scd_session.get("/subscriptions/{}".format(ids(SUB_TYPE))) + resp = scd_session.get(f"/subscriptions/{ids(SUB_TYPE)}") assert resp.status_code == 404, resp.content diff --git a/monitoring/prober/scd/test_subscription_update_validation.py b/monitoring/prober/scd/test_subscription_update_validation.py index b7f80e4fbe..a1c6973d2f 100644 --- a/monitoring/prober/scd/test_subscription_update_validation.py +++ b/monitoring/prober/scd/test_subscription_update_validation.py @@ -76,7 +76,7 @@ def test_ensure_clean_workspace(ids, scd_api, scd_session): def test_create_op(ids, scd_api, scd_session): entity_name = "operational_intent_reference" req = _make_op_req() - resp = scd_session.put("/{}s/{}".format(entity_name, ids(OP_TYPE)), json=req) + resp = scd_session.put(f"/{entity_name}s/{ids(OP_TYPE)}", json=req) assert resp.status_code == 201, resp.content data = resp.json() @@ -98,7 +98,7 @@ def test_create_op(ids, scd_api, scd_session): global sub_id sub_id = op["subscription_id"] - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content @@ -108,7 +108,7 @@ def test_create_op(ids, scd_api, scd_session): @depends_on(test_create_op) def test_mutate_sub_shrink_2d(scd_api, scd_session): # GET current sub before mutation - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) assert existing_sub is not None @@ -130,7 +130,7 @@ def test_mutate_sub_shrink_2d(scd_api, scd_session): @depends_on(test_create_op) def test_mutate_sub_shrink_altitude(scd_api, scd_session): # GET current sub before mutation - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) assert existing_sub is not None @@ -152,7 +152,7 @@ def test_mutate_sub_shrink_altitude(scd_api, scd_session): @depends_on(test_create_op) def test_mutate_sub_shrink_time(scd_api, scd_session): # GET current sub before mutation - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) assert existing_sub is not None @@ -174,7 +174,7 @@ def test_mutate_sub_shrink_time(scd_api, scd_session): @depends_on(test_create_op) def test_mutate_sub_not_shrink(scd_api, scd_session): # GET current sub before mutation - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content existing_sub = resp.json().get("subscription", None) assert existing_sub is not None @@ -203,12 +203,10 @@ def test_mutate_sub_not_shrink(scd_api, scd_session): @default_scope(SCOPE_SC) @depends_on(test_mutate_sub_not_shrink) def test_delete_op(ids, scd_api, scd_session): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 200, resp.content ovn = resp.json()["operational_intent_reference"]["ovn"] - resp = scd_session.delete( - "/operational_intent_references/{}/{}".format(ids(OP_TYPE), ovn) - ) + resp = scd_session.delete(f"/operational_intent_references/{ids(OP_TYPE)}/{ovn}") assert resp.status_code == 200, resp.content @@ -216,7 +214,7 @@ def test_delete_op(ids, scd_api, scd_session): @default_scope(SCOPE_SC) @depends_on(test_delete_op) def test_get_deleted_op_by_id(ids, scd_api, scd_session): - resp = scd_session.get("/operational_intent_references/{}".format(ids(OP_TYPE))) + resp = scd_session.get(f"/operational_intent_references/{ids(OP_TYPE)}") assert resp.status_code == 404, resp.content @@ -224,10 +222,10 @@ def test_get_deleted_op_by_id(ids, scd_api, scd_session): @default_scope(SCOPE_SC) @depends_on(test_create_op) def test_delete_sub(scd_api, scd_session): - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 200, resp.content version = resp.json()["subscription"]["version"] - resp = scd_session.delete("/subscriptions/{}/{}".format(sub_id, version)) + resp = scd_session.delete(f"/subscriptions/{sub_id}/{version}") assert resp.status_code == 200, resp.content @@ -235,7 +233,7 @@ def test_delete_sub(scd_api, scd_session): @default_scope(SCOPE_SC) @depends_on(test_create_op) def test_get_deleted_sub_by_id(scd_api, scd_session): - resp = scd_session.get("/subscriptions/{}".format(sub_id)) + resp = scd_session.get(f"/subscriptions/{sub_id}") assert resp.status_code == 404, resp.content diff --git a/monitoring/prober/utils.py b/monitoring/prober/utils.py index 0e69f3d913..441967f070 100644 --- a/monitoring/prober/utils.py +++ b/monitoring/prober/utils.py @@ -53,7 +53,7 @@ def encode_owner(owner_name: str) -> str: for letter in string_val: ord_val = get_ord_val(letter) bits += dec_to_bin(ord_val) - return "".join((bin_to_hex(s) for s in split_by(bits))) + return "".join(bin_to_hex(s) for s in split_by(bits)) def encode_resource_type_code(resource_type: int) -> str: diff --git a/monitoring/uss_qualifier/action_generators/action_generator.py b/monitoring/uss_qualifier/action_generators/action_generator.py index 21f4f86ec9..960b30bc77 100644 --- a/monitoring/uss_qualifier/action_generators/action_generator.py +++ b/monitoring/uss_qualifier/action_generators/action_generator.py @@ -2,7 +2,8 @@ import inspect from abc import ABC, abstractmethod -from typing import Any, Dict, Generic, Iterator, List, Optional, Type, TypeVar +from collections.abc import Iterator +from typing import Any, TypeVar from implicitdict import ImplicitDict @@ -23,14 +24,14 @@ from monitoring.uss_qualifier.resources.resource import ResourceType -class ActionGenerator(ABC, Generic[ActionGeneratorSpecificationType]): +class ActionGenerator[ActionGeneratorSpecificationType](ABC): definition: ActionGeneratorDefinition @abstractmethod def __init__( self, specification: ActionGeneratorSpecificationType, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): """Create an instance of the action generator. @@ -56,8 +57,8 @@ def actions(self) -> Iterator[Any]: @classmethod def list_potential_actions( - cls, specification: Optional[ActionGeneratorSpecificationType] - ) -> List[PotentialGeneratedAction]: + cls, specification: ActionGeneratorSpecificationType | None + ) -> list[PotentialGeneratedAction]: """Enumerate the potential actions that may be run by an instance of this generator type. Concrete subclasses of ActionGenerator must implement a classmethod that shadows this one according to this @@ -86,7 +87,7 @@ def get_name(cls) -> str: @staticmethod def make_from_definition( - definition: ActionGeneratorDefinition, resources: Dict[ResourceID, ResourceType] + definition: ActionGeneratorDefinition, resources: dict[ResourceID, ResourceType] ) -> ActionGeneratorType: action_generator_type = action_generator_type_from_name( definition.generator_type @@ -109,7 +110,7 @@ def make_from_definition( def action_generator_type_from_name( action_generator_type_name: GeneratorTypeName, -) -> Type[ActionGenerator]: +) -> type[ActionGenerator]: from monitoring.uss_qualifier import action_generators as action_generators_module import_submodules(action_generators_module) @@ -119,16 +120,14 @@ def action_generator_type_from_name( ) if not issubclass(action_generator_type, ActionGenerator): raise NotImplementedError( - "Action generator type {} is not a subclass of the ActionGenerator base class".format( - action_generator_type.__name__ - ) + f"Action generator type {action_generator_type.__name__} is not a subclass of the ActionGenerator base class" ) return action_generator_type def action_generator_specification_type( - action_generator_type: Type[ActionGenerator], -) -> Optional[Type]: + action_generator_type: type[ActionGenerator], +) -> type | None: constructor_signature = inspect.signature(action_generator_type.__init__) specification_type = None for arg_name, arg in constructor_signature.parameters.items(): diff --git a/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss.py b/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss.py index a25c0475df..0de0f72f15 100644 --- a/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss.py +++ b/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterator, List +from collections.abc import Iterator from implicitdict import ImplicitDict @@ -34,13 +34,13 @@ class ForEachDSSSpecification(ImplicitDict): class ForEachDSS(ActionGenerator[ForEachDSSSpecification]): - _actions: List[TestSuiteAction] + _actions: list[TestSuiteAction] _current_action: int @classmethod def list_potential_actions( cls, specification: ForEachDSSSpecification - ) -> List[PotentialGeneratedAction]: + ) -> list[PotentialGeneratedAction]: return list_potential_actions_for_action_declaration( specification.action_to_repeat ) @@ -52,7 +52,7 @@ def get_name(cls) -> str: def __init__( self, specification: ForEachDSSSpecification, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): if specification.dss_instances_source not in resources: raise MissingResourceError( @@ -85,5 +85,4 @@ def __init__( self._current_action = 0 def actions(self) -> Iterator[TestSuiteAction]: - for a in self._actions: - yield a + yield from self._actions diff --git a/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss.py b/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss.py index 15326c8cac..d3f78965bc 100644 --- a/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss.py +++ b/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterator, List +from collections.abc import Iterator from implicitdict import ImplicitDict @@ -33,13 +33,13 @@ class ForEachDSSSpecification(ImplicitDict): class ForEachDSS(ActionGenerator[ForEachDSSSpecification]): - _actions: List[TestSuiteAction] + _actions: list[TestSuiteAction] _current_action: int @classmethod def list_potential_actions( cls, specification: ForEachDSSSpecification - ) -> List[PotentialGeneratedAction]: + ) -> list[PotentialGeneratedAction]: return list_potential_actions_for_action_declaration( specification.action_to_repeat ) @@ -51,7 +51,7 @@ def get_name(cls) -> str: def __init__( self, specification: ForEachDSSSpecification, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): if specification.dss_instances_source not in resources: raise MissingResourceError( @@ -79,5 +79,4 @@ def __init__( self._current_action = 0 def actions(self) -> Iterator[TestSuiteAction]: - for a in self._actions: - yield a + yield from self._actions diff --git a/monitoring/uss_qualifier/action_generators/definitions.py b/monitoring/uss_qualifier/action_generators/definitions.py index e59c13973c..d1c520f2a3 100644 --- a/monitoring/uss_qualifier/action_generators/definitions.py +++ b/monitoring/uss_qualifier/action_generators/definitions.py @@ -1,4 +1,4 @@ -from typing import Dict, TypeVar +from typing import TypeVar from implicitdict import ImplicitDict @@ -20,7 +20,7 @@ class ActionGeneratorDefinition(ImplicitDict): specification: dict = {} """Specification of action generator; format is the ActionGeneratorSpecificationType that corresponds to the `generator_type`""" - resources: Dict[ResourceID, ResourceID] + resources: dict[ResourceID, ResourceID] """Mapping of the ID a resource will be known by in the child action -> the ID a resource is known by in the parent test suite. The child action resource ID is supplied by the parent test suite resource ID . diff --git a/monitoring/uss_qualifier/action_generators/documentation/definitions.py b/monitoring/uss_qualifier/action_generators/documentation/definitions.py index 54cd610645..a5c7cfe47b 100644 --- a/monitoring/uss_qualifier/action_generators/documentation/definitions.py +++ b/monitoring/uss_qualifier/action_generators/documentation/definitions.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict from monitoring.uss_qualifier.action_generators.definitions import GeneratorTypeName @@ -17,10 +15,10 @@ class PotentialTestScenarioAction(ImplicitDict): class PotentialTestSuiteAction(ImplicitDict): - suite_type: Optional[TestSuiteTypeName] + suite_type: TestSuiteTypeName | None """Type/location of test suite. Usually expressed as the file name of the suite definition (without extension) qualified relative to the `uss_qualifier` folder""" - suite_definition: Optional[TestSuiteDefinition] + suite_definition: TestSuiteDefinition | None """Definition of test suite internal to the configuration -- specified instead of `suite_type`.""" @@ -33,9 +31,9 @@ class PotentialActionGeneratorAction(ImplicitDict): class PotentialGeneratedAction(ImplicitDict): - test_scenario: Optional[PotentialTestScenarioAction] - test_suite: Optional[PotentialTestSuiteAction] - action_generator: Optional[PotentialActionGeneratorAction] + test_scenario: PotentialTestScenarioAction | None + test_suite: PotentialTestSuiteAction | None + action_generator: PotentialActionGeneratorAction | None def get_action_type(self) -> ActionType: matches = [v for v in ActionType if v in self and self[v]] diff --git a/monitoring/uss_qualifier/action_generators/documentation/documentation.py b/monitoring/uss_qualifier/action_generators/documentation/documentation.py index 9de2f70b8b..e168427407 100644 --- a/monitoring/uss_qualifier/action_generators/documentation/documentation.py +++ b/monitoring/uss_qualifier/action_generators/documentation/documentation.py @@ -1,5 +1,3 @@ -from typing import List, Union - from implicitdict import ImplicitDict from monitoring.uss_qualifier.action_generators.action_generator import ( @@ -22,8 +20,8 @@ def list_potential_actions_for_action_generator_definition( - generator_def: Union[ActionGeneratorDefinition, PotentialActionGeneratorAction], -) -> List[PotentialGeneratedAction]: + generator_def: ActionGeneratorDefinition | PotentialActionGeneratorAction, +) -> list[PotentialGeneratedAction]: action_generator_type = action_generator_type_from_name( generator_def.generator_type ) @@ -37,7 +35,7 @@ def list_potential_actions_for_action_generator_definition( def list_potential_actions_for_action_declaration( declaration: TestSuiteActionDeclaration, -) -> List[PotentialGeneratedAction]: +) -> list[PotentialGeneratedAction]: action_type = declaration.get_action_type() if action_type == ActionType.TestScenario: return [ diff --git a/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py b/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py index 705ce6f0fb..2ec548db50 100644 --- a/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py +++ b/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterator, List, Optional +from collections.abc import Iterator from implicitdict import ImplicitDict @@ -30,23 +30,23 @@ class FlightPlannerCombinationsSpecification(ImplicitDict): flight_planners_source: ResourceID """ID of the resource providing all available flight planners""" - combination_selector_source: Optional[ResourceID] = None + combination_selector_source: ResourceID | None = None """If specified and contained in the provided resources, the resource containing a FlightPlannerCombinationSelectorResource to select only a subset of combinations""" - roles: List[ResourceID] + roles: list[ResourceID] """Resource IDs of FlightPlannerResource inputs to the action_to_repeat""" class FlightPlannerCombinations( ActionGenerator[FlightPlannerCombinationsSpecification] ): - _actions: List[TestSuiteAction] + _actions: list[TestSuiteAction] _current_action: int @classmethod def list_potential_actions( cls, specification: FlightPlannerCombinationsSpecification - ) -> List[PotentialGeneratedAction]: + ) -> list[PotentialGeneratedAction]: return list_potential_actions_for_action_declaration( specification.action_to_repeat ) @@ -58,7 +58,7 @@ def get_name(cls) -> str: def __init__( self, specification: FlightPlannerCombinationsSpecification, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): if specification.flight_planners_source not in resources: raise MissingResourceError( @@ -122,5 +122,4 @@ def __init__( self._current_action = 0 def actions(self) -> Iterator[TestSuiteAction]: - for a in self._actions: - yield a + yield from self._actions diff --git a/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality.py b/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality.py index 6efee691b6..2240f00dc3 100644 --- a/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality.py +++ b/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterator, List +from collections.abc import Iterator from implicitdict import ImplicitDict @@ -47,13 +47,13 @@ class WithLocality(ActionGenerator[WithLocalitySpecification]): """Performs a specified test suite action after first configuring mock_uss instances to use a specified locality, and then restoring the original locality afterward.""" - _actions: List[TestSuiteAction] + _actions: list[TestSuiteAction] _current_action: int @classmethod def list_potential_actions( cls, specification: WithLocalitySpecification - ) -> List[PotentialGeneratedAction]: + ) -> list[PotentialGeneratedAction]: actions = [ PotentialGeneratedAction( test_scenario=PotentialTestScenarioAction( @@ -80,7 +80,7 @@ def get_name(cls) -> str: def __init__( self, specification: WithLocalitySpecification, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): if specification.mock_uss_instances_source not in resources: raise ValueError( @@ -136,5 +136,4 @@ def __init__( self._current_action = 0 def actions(self) -> Iterator[TestSuiteAction]: - for a in self._actions: - yield a + yield from self._actions diff --git a/monitoring/uss_qualifier/action_generators/repetition/repeat.py b/monitoring/uss_qualifier/action_generators/repetition/repeat.py index 82ca11f08f..1a24a964d5 100644 --- a/monitoring/uss_qualifier/action_generators/repetition/repeat.py +++ b/monitoring/uss_qualifier/action_generators/repetition/repeat.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterator, List +from collections.abc import Iterator from implicitdict import ImplicitDict @@ -23,13 +23,13 @@ class RepeatSpecification(ImplicitDict): class Repeat(ActionGenerator[RepeatSpecification]): - _actions: List[TestSuiteAction] + _actions: list[TestSuiteAction] _current_action: int @classmethod def list_potential_actions( cls, specification: RepeatSpecification - ) -> List[PotentialGeneratedAction]: + ) -> list[PotentialGeneratedAction]: return list_potential_actions_for_action_declaration( specification.action_to_repeat ) @@ -37,7 +37,7 @@ def list_potential_actions( def __init__( self, specification: RepeatSpecification, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): self._actions = [ TestSuiteAction(specification.action_to_repeat, resources) @@ -46,5 +46,4 @@ def __init__( self._current_action = 0 def actions(self) -> Iterator[TestSuiteAction]: - for a in self._actions: - yield a + yield from self._actions diff --git a/monitoring/uss_qualifier/common_data_definitions.py b/monitoring/uss_qualifier/common_data_definitions.py index 6ce18f2a20..d8f80cd252 100644 --- a/monitoring/uss_qualifier/common_data_definitions.py +++ b/monitoring/uss_qualifier/common_data_definitions.py @@ -1,7 +1,6 @@ from __future__ import annotations from enum import Enum -from typing import List class Severity(str, Enum): @@ -99,5 +98,5 @@ def symbol(self) -> str: }.get(self.value, "�") @staticmethod - def all_values() -> List[Severity]: + def all_values() -> list[Severity]: return [Severity.Low, Severity.Medium, Severity.High, Severity.Critical] diff --git a/monitoring/uss_qualifier/configurations/configuration.py b/monitoring/uss_qualifier/configurations/configuration.py index b0965463a5..3c88d6a37c 100644 --- a/monitoring/uss_qualifier/configurations/configuration.py +++ b/monitoring/uss_qualifier/configurations/configuration.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Dict, List, Optional - from implicitdict import ImplicitDict from monitoring.monitorlib.dicts import JSONAddress @@ -22,13 +20,13 @@ class InstanceIndexRange(ImplicitDict): - lo: Optional[int] + lo: int | None """If specified, no indices lower than this value will be included in the range.""" - i: Optional[int] + i: int | None """If specified, no index other than this one will be included in the range.""" - hi: Optional[int] + hi: int | None """If specified, no indices higher than this value will be included in the range.""" def includes(self, i: int) -> bool: @@ -44,28 +42,28 @@ def includes(self, i: int) -> bool: class ActionGeneratorSelectionCondition(ImplicitDict): """By default, select all action generators. When specified, limit selection to specified conditions.""" - types: Optional[List[GeneratorTypeName]] + types: list[GeneratorTypeName] | None """Only select action generators of the specified types.""" class TestSuiteSelectionCondition(ImplicitDict): """By default, select all test suites. When specified, limit selection to specified conditions.""" - types: Optional[List[TestSuiteTypeName]] + types: list[TestSuiteTypeName] | None """Only select test suites of the specified types.""" class TestScenarioSelectionCondition(ImplicitDict): """By default, select all test scenarios. When specified, limit selection to specified conditions.""" - types: Optional[List[TestScenarioTypeName]] + types: list[TestScenarioTypeName] | None """Only select test scenarios of the specified types.""" class NthInstanceCondition(ImplicitDict): """Select an action once a certain number of matching instances have happened.""" - n: List[InstanceIndexRange] + n: list[InstanceIndexRange] """Only select an action if it is one of these nth instances.""" where_action: TestSuiteActionSelectionCondition @@ -75,12 +73,12 @@ class NthInstanceCondition(ImplicitDict): class AncestorSelectionCondition(ImplicitDict): """Select ancestor actions meeting all the specified conditions.""" - of_generation: Optional[int] + of_generation: int | None """The ancestor is exactly this many generations removed (1 = parent, 2 = grandparent, etc). If not specified, an ancestor of any generation meeting the `which` conditions will be selected.""" - which: List[TestSuiteActionSelectionCondition] + which: list[TestSuiteActionSelectionCondition] """Only select an ancestor meeting ALL of these conditions.""" @@ -90,19 +88,19 @@ class TestSuiteActionSelectionCondition(ImplicitDict): If more than one subcondition is specified, satisfaction of ALL subconditions are necessary to select the action. """ - is_action_generator: Optional[ActionGeneratorSelectionCondition] + is_action_generator: ActionGeneratorSelectionCondition | None """Select these action generator actions.""" - is_test_suite: Optional[TestSuiteSelectionCondition] + is_test_suite: TestSuiteSelectionCondition | None """Select these test suite actions.""" - is_test_scenario: Optional[TestScenarioSelectionCondition] + is_test_scenario: TestScenarioSelectionCondition | None """Select these test scenario actions.""" - regex_matches_name: Optional[str] + regex_matches_name: str | None """Select actions where this regular expression has a match in the action's name.""" - defined_at: Optional[List[JSONAddress]] + defined_at: list[JSONAddress] | None """Select actions defined at one of the specified addresses. The top-level action in a test run is 'test_scenario', 'test_suite', or 'action_generator'. Children use the @@ -111,27 +109,27 @@ class TestSuiteActionSelectionCondition(ImplicitDict): 'action_generator.actions[1].test_suite.actions[2].test_scenario'. An address that starts or ends with 'actions[i]' is invalid and will never match.""" - nth_instance: Optional[NthInstanceCondition] + nth_instance: NthInstanceCondition | None """Select only certain instances of matching actions.""" - has_ancestor: Optional[AncestorSelectionCondition] + has_ancestor: AncestorSelectionCondition | None """Select only actions with a matching ancestor.""" - except_when: Optional[List[TestSuiteActionSelectionCondition]] + except_when: list[TestSuiteActionSelectionCondition] | None """Do not select actions selected by any of these conditions, even when they are selected by one or more conditions above.""" class ExecutionConfiguration(ImplicitDict): - include_action_when: Optional[List[TestSuiteActionSelectionCondition]] = None + include_action_when: list[TestSuiteActionSelectionCondition] | None = None """If specified, only execute test actions if they are selected by ANY of these conditions (and not selected by any of the `skip_when` conditions).""" - skip_action_when: Optional[List[TestSuiteActionSelectionCondition]] = None + skip_action_when: list[TestSuiteActionSelectionCondition] | None = None """If specified, do not execute test actions if they are selected by ANY of these conditions.""" - stop_fast: Optional[bool] = False + stop_fast: bool | None = False """If true, escalate the Severity of any failed check to Critical in order to end the test run early.""" - stop_when_resource_not_created: Optional[bool] = False + stop_when_resource_not_created: bool | None = False """If true, stop test execution if one of the resources cannot be created. Otherwise, resources that cannot be created due to missing prerequisites are simply treated as omitted.""" @@ -139,13 +137,13 @@ class TestConfiguration(ImplicitDict): action: TestSuiteActionDeclaration """The action this test configuration wants to run (usually a test suite)""" - non_baseline_inputs: Optional[List[JSONAddress]] = None + non_baseline_inputs: list[JSONAddress] | None = None """List of portions of the configuration that should not be considered when computing the test baseline signature (e.g., environmental definitions).""" resources: ResourceCollection """Declarations for resources used by the test suite""" - execution: Optional[ExecutionConfiguration] + execution: ExecutionConfiguration | None """Specification for how to execute the test run.""" @@ -157,20 +155,20 @@ class TestedRequirementsConfiguration(ImplicitDict): report_name: str """Name of subfolder in output path to contain the rendered templated report""" - requirement_collections: Optional[ - Dict[TestedRequirementsCollectionIdentifier, RequirementCollection] - ] + requirement_collections: ( + dict[TestedRequirementsCollectionIdentifier, RequirementCollection] | None + ) """Definition of requirement collections specific to production of this artifact.""" - aggregate_participants: Optional[Dict[ParticipantID, List[ParticipantID]]] + aggregate_participants: dict[ParticipantID, list[ParticipantID]] | None """If specified, a list of 'aggregate participants', each of which is composed of multiple test participants. If specified, these aggregate participants are the preferred subject for `participant_requirements`. """ - participant_requirements: Optional[ - Dict[ParticipantID, Optional[TestedRequirementsCollectionIdentifier]] - ] + participant_requirements: ( + dict[ParticipantID, TestedRequirementsCollectionIdentifier | None] | None + ) """If a requirement collection is specified for a participant, only the requirements in the specified collection will be listed on that participant's report. If a requirement collection is specified as None/null for a participant, all potentially-testable requirements will be included. @@ -203,7 +201,7 @@ class TemplatedReportConfiguration(ImplicitDict): report_name: str """Name of HTML file (without extension) to contain the rendered templated report""" - configuration: Optional[TemplatedReportInjectedConfiguration] = None + configuration: TemplatedReportInjectedConfiguration | None = None """Configuration to be injected in the templated report""" @@ -211,7 +209,7 @@ class RawReportConfiguration(ImplicitDict): redact_access_tokens: bool = True """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" - indent: Optional[int] = None + indent: int | None = None """To pretty-print JSON content, specify an indent level (generally 2), or omit or set to None to write compactly.""" @@ -221,36 +219,36 @@ class GloballyExpandedReportConfiguration(ImplicitDict): class ArtifactsConfiguration(ImplicitDict): - raw_report: Optional[RawReportConfiguration] = None + raw_report: RawReportConfiguration | None = None """Configuration for raw report generation""" - report_html: Optional[ReportHTMLConfiguration] = None + report_html: ReportHTMLConfiguration | None = None """If specified, configuration describing how an HTML version of the raw report should be generated""" - templated_reports: Optional[List[TemplatedReportConfiguration]] = None + templated_reports: list[TemplatedReportConfiguration] | None = None """List of report templates to be rendered""" - tested_requirements: Optional[List[TestedRequirementsConfiguration]] = None + tested_requirements: list[TestedRequirementsConfiguration] | None = None """If specified, list of configurations describing desired reports summarizing tested requirements for each participant""" - sequence_view: Optional[SequenceViewConfiguration] = None + sequence_view: SequenceViewConfiguration | None = None """If specified, configuration describing a desired report describing the sequence of events that occurred during the test""" - globally_expanded_report: Optional[GloballyExpandedReportConfiguration] = None + globally_expanded_report: GloballyExpandedReportConfiguration | None = None """If specified, configuration describing a desired report mimicking what might be seen had the test run been conducted manually.""" class USSQualifierConfigurationV1(ImplicitDict): - test_run: Optional[TestConfiguration] = None + test_run: TestConfiguration | None = None """If specified, configuration describing how to perform a test run""" - artifacts: Optional[ArtifactsConfiguration] = None + artifacts: ArtifactsConfiguration | None = None """If specified, configuration describing the artifacts related to the test run""" - validation: Optional[ValidationConfiguration] = None + validation: ValidationConfiguration | None = None """If specified, configuration describing how to validate the output report (and return an error code if validation fails)""" class USSQualifierConfiguration(ImplicitDict): - v1: Optional[USSQualifierConfigurationV1] + v1: USSQualifierConfigurationV1 | None """Configuration in version 1 format""" diff --git a/monitoring/uss_qualifier/documentation.py b/monitoring/uss_qualifier/documentation.py index a0e0beac26..9729eb54b7 100644 --- a/monitoring/uss_qualifier/documentation.py +++ b/monitoring/uss_qualifier/documentation.py @@ -21,5 +21,5 @@ def text_of(value: marko.element.Element) -> str: return result else: raise NotImplementedError( - "Cannot yet extract raw text from {}".format(value.__class__.__name__) + f"Cannot yet extract raw text from {value.__class__.__name__}" ) diff --git a/monitoring/uss_qualifier/fileio.py b/monitoring/uss_qualifier/fileio.py index f6fff28ddf..1896bf81f0 100644 --- a/monitoring/uss_qualifier/fileio.py +++ b/monitoring/uss_qualifier/fileio.py @@ -2,7 +2,6 @@ import json import os import re -from typing import Dict, List, Optional, Tuple, Union import _jsonnet import bc_jsonpath_ng @@ -126,7 +125,7 @@ def _load_content_from_file_name(file_name: str) -> str: # http(s):// web file reference file_content = _get_web_content(file_name) else: - with open(file_name, "r") as f: + with open(file_name) as f: file_content = f.read() return file_content @@ -136,7 +135,7 @@ def load_content(data_file: FileReference) -> str: return _load_content_from_file_name(resolve_filename(data_file)) -def _split_anchor(file_name: str) -> Tuple[str, Optional[str]]: +def _split_anchor(file_name: str) -> tuple[str, str | None]: if "#" in file_name: anchor_location = file_name.index("#") base_file_name = file_name[0:anchor_location] @@ -171,8 +170,8 @@ def load_dict_with_references(data_file: FileReference) -> dict: def _jsonnet_import_callback( - base_file_name: str, folder: str, rel: str, cache: Optional[Dict[str, dict]] -) -> Tuple[str, bytes]: + base_file_name: str, folder: str, rel: str, cache: dict[str, dict] | None +) -> tuple[str, bytes]: if rel.endswith(".libsonnet"): # Do not attempt to parse libsonnet content (e.g., resolve $refs); # it will be parsed after loading the full top-level Jsonnet. @@ -187,8 +186,8 @@ def _jsonnet_import_callback( def _load_dict_with_references_from_file_name( - file_name: str, context_file_name: str, cache: Optional[Dict[str, dict]] = None -) -> Tuple[dict, str]: + file_name: str, context_file_name: str, cache: dict[str, dict] | None = None +) -> tuple[dict, str]: if cache is None: cache = {} @@ -278,7 +277,7 @@ def _is_descendant(potential_descendant: str, ancestor: str) -> bool: return result -def _identify_refs(content: dict) -> List[str]: +def _identify_refs(content: dict) -> list[str]: refs = _find_refs(content) external_refs = [k for k, v in refs.items() if not v.startswith("#")] remaining_internal_refs = {k: v for k, v in refs.items() if v.startswith("#")} @@ -310,7 +309,7 @@ def _identify_refs(content: dict) -> List[str]: return external_refs + internal_refs -def _find_refs(content: Union[dict, list], root: str = "$") -> Dict[str, str]: +def _find_refs(content: dict | list, root: str = "$") -> dict[str, str]: paths = {} if isinstance(content, dict): if "$ref" in content and isinstance(content["$ref"], str): @@ -328,9 +327,9 @@ def _find_refs(content: Union[dict, list], root: str = "$") -> Dict[str, str]: def _replace_refs( content: dict, context_file_name: str, - ref_parent_paths: List[str], - allof_paths: List[str], - cache: Optional[Dict[str, dict]] = None, + ref_parent_paths: list[str], + allof_paths: list[str], + cache: dict[str, dict] | None = None, ) -> None: for path in ref_parent_paths: parent = [m.value for m in bc_jsonpath_ng.parse(path).find(content)] @@ -401,7 +400,7 @@ def _select_path(content: dict, path: str) -> dict: return _select_path(content[component], subpath) -def _identify_allofs(content: Union[dict, list], root: str = "$") -> List[str]: +def _identify_allofs(content: dict | list, root: str = "$") -> list[str]: paths = [] if isinstance(content, dict): if ( diff --git a/monitoring/uss_qualifier/main.py b/monitoring/uss_qualifier/main.py index 533c745d8f..c387e7b1a6 100644 --- a/monitoring/uss_qualifier/main.py +++ b/monitoring/uss_qualifier/main.py @@ -4,7 +4,6 @@ import json import os import sys -from typing import Optional import yaml from implicitdict import ImplicitDict @@ -88,8 +87,8 @@ class TestDefinitionDescription(ImplicitDict): codebase_version: str commit_hash: str - baseline_signature: Optional[str] = None - environment_signature: Optional[str] = None + baseline_signature: str | None = None + environment_signature: str | None = None def sign(self, whole_config: USSQualifierConfiguration) -> None: logger.debug("Computing signatures of inputs") @@ -170,8 +169,8 @@ def run_config( config_output: str, skip_validation: bool, exit_before_execution: bool, - output_path: Optional[str], - runtime_metadata: Optional[dict], + output_path: str | None, + runtime_metadata: dict | None, disallow_unredacted: bool, ): config_src = load_dict_with_references(config_name) diff --git a/monitoring/uss_qualifier/reports/capabilities.py b/monitoring/uss_qualifier/reports/capabilities.py index af1f566474..02066ddae7 100644 --- a/monitoring/uss_qualifier/reports/capabilities.py +++ b/monitoring/uss_qualifier/reports/capabilities.py @@ -1,4 +1,5 @@ -from typing import Any, Callable, Dict, List, Optional, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar import bc_jsonpath_ng.ext @@ -34,10 +35,12 @@ [SpecificConditionType, ParticipantID, TestSuiteReport], ParticipantCapabilityConditionEvaluationReport, ] -_capability_condition_evaluators: Dict[SpecificConditionType, ConditionEvaluator] = {} +_capability_condition_evaluators: dict[SpecificConditionType, ConditionEvaluator] = {} -def capability_condition_evaluator(condition_type: SpecificConditionType): +def capability_condition_evaluator[SpecificConditionType: SpecificCondition]( + condition_type: SpecificConditionType, +): """Decorator to label a function that evaluates a specific condition for verifying a capability. Args: @@ -94,8 +97,8 @@ def evaluate_condition_for_test_suite( def evaluate_all_conditions_condition( condition: AllConditions, participant_id: ParticipantID, report: TestSuiteReport ) -> ParticipantCapabilityConditionEvaluationReport: - satisfied_conditions: List[ParticipantCapabilityConditionEvaluationReport] = [] - unsatisfied_conditions: List[ParticipantCapabilityConditionEvaluationReport] = [] + satisfied_conditions: list[ParticipantCapabilityConditionEvaluationReport] = [] + unsatisfied_conditions: list[ParticipantCapabilityConditionEvaluationReport] = [] for subcondition in condition.conditions: subreport = evaluate_condition_for_test_suite( subcondition, participant_id, report @@ -118,8 +121,8 @@ def evaluate_all_conditions_condition( def evaluate_any_condition_condition( condition: AnyCondition, participant_id: ParticipantID, report: TestSuiteReport ) -> ParticipantCapabilityConditionEvaluationReport: - satisfied_options: List[ParticipantCapabilityConditionEvaluationReport] = [] - unsatisfied_options: List[ParticipantCapabilityConditionEvaluationReport] = [] + satisfied_options: list[ParticipantCapabilityConditionEvaluationReport] = [] + unsatisfied_options: list[ParticipantCapabilityConditionEvaluationReport] = [] for subcondition in condition.conditions: subreport = evaluate_condition_for_test_suite( subcondition, participant_id, report @@ -159,7 +162,7 @@ def evaluate_requirements_checked_conditions( participant_id: ParticipantID, report: TestSuiteReport, ) -> ParticipantCapabilityConditionEvaluationReport: - req_checks: Dict[RequirementID, CheckedRequirement] = { + req_checks: dict[RequirementID, CheckedRequirement] = { req_id: CheckedRequirement( requirement_id=req_id, passed_checks=[], failed_checks=[] ) @@ -193,7 +196,7 @@ def evaluate_requirements_checked_conditions( ) -def _jsonpath_of(descendant: Any, ancestor: Any) -> Optional[str]: +def _jsonpath_of(descendant: Any, ancestor: Any) -> str | None: """Construct a relative JSONPath to descendant from ancestor by exhaustive reference equality search. One would think this functionality would be part of the jsonpath_ng package when producing matches, but one would diff --git a/monitoring/uss_qualifier/reports/capability_definitions.py b/monitoring/uss_qualifier/reports/capability_definitions.py index 6339209ad3..5fbdafe098 100644 --- a/monitoring/uss_qualifier/reports/capability_definitions.py +++ b/monitoring/uss_qualifier/reports/capability_definitions.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import List, Optional - from implicitdict import ImplicitDict from monitoring.uss_qualifier.requirements.definitions import RequirementCollection @@ -23,7 +21,7 @@ class AllConditions(SpecificCondition): Note that an empty list of conditions will result in a successful evaluation.""" - conditions: List[CapabilityVerificationCondition] + conditions: list[CapabilityVerificationCondition] class AnyCondition(SpecificCondition): @@ -31,7 +29,7 @@ class AnyCondition(SpecificCondition): Note that an empty list of conditions will result in an unsuccessful evaluation.""" - conditions: List[CapabilityVerificationCondition] + conditions: list[CapabilityVerificationCondition] class RequirementsCheckedCondition(SpecificCondition): @@ -59,10 +57,10 @@ class CapabilityVerifiedCondition(SpecificCondition): Note that a capability which do not declare any requirement will result in an unsuccessful evaluation. """ - capability_ids: List[CapabilityID] + capability_ids: list[CapabilityID] """List of identifier of capability that must be verified for this condition to be satisfied.""" - capability_location: Optional[JSONPathExpression] + capability_location: JSONPathExpression | None """Location of report to inspect for the verification of the specified capability, relative to the report in which the capability is defined. Implicit default value is "$" (look for verified capability in the report in which the dependant capability is defined). @@ -85,11 +83,11 @@ class CapabilityVerificationCondition(ImplicitDict): Exactly one field must be specified.""" - all_conditions: Optional[AllConditions] - any_conditions: Optional[AnyCondition] - no_failed_checks: Optional[NoFailedChecksCondition] - requirements_checked: Optional[RequirementsCheckedCondition] - capability_verified: Optional[CapabilityVerifiedCondition] + all_conditions: AllConditions | None + any_conditions: AnyCondition | None + no_failed_checks: NoFailedChecksCondition | None + requirements_checked: RequirementsCheckedCondition | None + capability_verified: CapabilityVerifiedCondition | None class ParticipantCapabilityDefinition(ImplicitDict): diff --git a/monitoring/uss_qualifier/reports/globally_expanded/generate.py b/monitoring/uss_qualifier/reports/globally_expanded/generate.py index a24941265f..79b2036245 100644 --- a/monitoring/uss_qualifier/reports/globally_expanded/generate.py +++ b/monitoring/uss_qualifier/reports/globally_expanded/generate.py @@ -1,6 +1,6 @@ import os +from collections.abc import Iterator, Sequence from dataclasses import dataclass -from typing import Iterator, List, Sequence import marko import marko.element @@ -112,7 +112,7 @@ def generate_globally_expanded_report( resource_pool = make_resources_config(report.configuration.v1.test_run) - def indented_ul(value) -> List[str]: + def indented_ul(value) -> list[str]: result = [] if isinstance(value, dict): for k, v in value.items(): @@ -174,7 +174,6 @@ def describe_pool_resource(k: str, v: dict) -> str: os.path.join( os.path.dirname(__file__), "../templates/globally_expanded/style.html" ), - "r", ) as style: f.write(style.read()) f.write("\n") @@ -190,8 +189,7 @@ def _generate_sections(node: ActionNode) -> Iterator[_Section]: yield _generate_skipped_scenario_section(node) else: for child in node.children: - for subsection in _generate_sections(child): - yield subsection + yield from _generate_sections(child) def _generate_skipped_scenario_section(node: ActionNode) -> _Section: @@ -203,7 +201,7 @@ def _generate_skipped_scenario_section(node: ActionNode) -> _Section: def _generate_scenario_section(scenario: TestedScenario) -> _Section: doc_summary = get_documentation_by_name(scenario.type) - with open(doc_summary.local_path, "r") as f: + with open(doc_summary.local_path) as f: doc = marko.parse(f.read()) _modify_scenario_documentation( @@ -341,7 +339,7 @@ def _strip_link(element: marko.element.Element) -> None: def _get_test_step_fragment( absolute_path: str, parent_level: int ) -> marko.block.Document: - with open(absolute_path, "r") as f: + with open(absolute_path) as f: doc = marko.parse(f.read()) _remove_top_heading(doc) diff --git a/monitoring/uss_qualifier/reports/report.py b/monitoring/uss_qualifier/reports/report.py index 833dfc8784..815a649247 100644 --- a/monitoring/uss_qualifier/reports/report.py +++ b/monitoring/uss_qualifier/reports/report.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Callable, Iterator from datetime import UTC, datetime -from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Union +from typing import Any from implicitdict import ImplicitDict, StringBasedDateTime @@ -40,19 +41,19 @@ class FailedCheck(ImplicitDict): details: str """Human-readable description of the issue""" - requirements: List[RequirementID] + requirements: list[RequirementID] """Requirements that are not met due to this failed check""" severity: Severity """How severe the issue is""" - participants: List[ParticipantID] + participants: list[ParticipantID] """Participants that may not meet the relevant requirements due to this failed check""" - query_report_timestamps: Optional[List[str]] + query_report_timestamps: list[str] | None """List of the `report` timestamp field for queries relevant to this failed check""" - additional_data: Optional[dict] + additional_data: dict | None """Additional data, structured according to the checks' needs, that may be relevant for understanding this failed check""" @@ -63,10 +64,10 @@ class PassedCheck(ImplicitDict): timestamp: StringBasedDateTime """Time the issue was discovered""" - requirements: List[RequirementID] + requirements: list[RequirementID] """Requirements that would not have been met if this check had failed""" - participants: List[ParticipantID] + participants: list[ParticipantID] """Participants that may not have met the relevant requirements if this check had failed""" @@ -80,16 +81,16 @@ class TestStepReport(ImplicitDict): start_time: StringBasedDateTime """Time at which the test step started""" - queries: Optional[List[fetch.Query]] + queries: list[fetch.Query] | None """Description of HTTP requests relevant to this issue""" - failed_checks: List[FailedCheck] + failed_checks: list[FailedCheck] """The checks which failed in this test step""" - passed_checks: List[PassedCheck] + passed_checks: list[PassedCheck] """The checks which successfully passed in this test step""" - end_time: Optional[StringBasedDateTime] + end_time: StringBasedDateTime | None """Time at which the test step completed or encountered an error""" def has_critical_problem(self) -> bool: @@ -98,14 +99,14 @@ def has_critical_problem(self) -> bool: def successful(self) -> bool: return False if self.failed_checks else True - def participants_with_failed_checks(self) -> Set[str]: + def participants_with_failed_checks(self) -> set[str]: participants = set() for fc in self.failed_checks: for p in fc.participants: participants.add(p) return participants - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: participants = set() for pc in self.passed_checks: for p in pc.participants: @@ -116,20 +117,20 @@ def all_participants(self) -> Set[ParticipantID]: return participants def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, pc in enumerate(self.passed_checks): if participant_id is None or participant_id in pc.participants: yield f"passed_checks[{i}]", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, fc in enumerate(self.failed_checks): if participant_id is None or participant_id in fc.participants: yield f"failed_checks[{i}]", fc - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: ids = set() for pc in self.passed_checks: ids.update(pc.participants) @@ -148,36 +149,36 @@ class TestCaseReport(ImplicitDict): start_time: StringBasedDateTime """Time at which the test case started""" - end_time: Optional[StringBasedDateTime] + end_time: StringBasedDateTime | None """Time at which the test case completed or encountered an error""" - steps: List[TestStepReport] + steps: list[TestStepReport] """Reports for each of the test steps in this test case, in chronological order.""" def has_critical_problem(self): return any(s.has_critical_problem() for s in self.steps) - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: participants = set() for step in self.steps: participants = participants.union(step.all_participants()) return participants def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, step in enumerate(self.steps): for path, pc in step.query_passed_checks(participant_id): yield f"steps[{i}].{path}", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, step in enumerate(self.steps): for path, fc in step.query_failed_checks(participant_id): yield f"steps[{i}].{path}", fc - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: ids = set() for step in self.steps: ids.update(step.participant_ids()) @@ -222,28 +223,28 @@ class TestScenarioReport(ImplicitDict): documentation_url: str """URL at which this test scenario is described""" - resource_origins: Optional[Dict[ResourceID, str]] + resource_origins: dict[ResourceID, str] | None """For each resource used by this test scenario, the place that resource originated.""" - notes: Optional[Dict[str, Note]] + notes: dict[str, Note] | None """Additional information about this scenario that may be useful""" start_time: StringBasedDateTime """Time at which the test scenario started""" - end_time: Optional[StringBasedDateTime] + end_time: StringBasedDateTime | None """Time at which the test scenario completed or encountered an error""" successful: bool = False """True iff test scenario completed normally with no failed checks""" - cases: List[TestCaseReport] + cases: list[TestCaseReport] """Reports for each of the test cases in this test scenario, in chronological order.""" - cleanup: Optional[TestStepReport] + cleanup: TestStepReport | None """If this test scenario performed cleanup, this report captures the relevant information.""" - execution_error: Optional[ErrorReport] + execution_error: ErrorReport | None """If there was an error while executing this test scenario, this field describes the error""" def has_critical_problem(self) -> bool: @@ -255,7 +256,7 @@ def has_critical_problem(self) -> bool: return True return False - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: participants = set() for case in self.cases: participants = participants.union(case.all_participants()) @@ -264,8 +265,8 @@ def all_participants(self) -> Set[ParticipantID]: return participants def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, case in enumerate(self.cases): for path, pc in case.query_passed_checks(participant_id): yield f"cases[{i}].{path}", pc @@ -274,8 +275,8 @@ def query_passed_checks( yield f"cleanup.{path}", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, FailedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, FailedCheck]]: for i, case in enumerate(self.cases): for path, fc in case.query_failed_checks(participant_id): yield f"cases[{i}].{path}", fc @@ -283,7 +284,7 @@ def query_failed_checks( for path, fc in self.cleanup.query_failed_checks(participant_id): yield f"cleanup.{path}", fc - def queries(self) -> List[fetch.Query]: + def queries(self) -> list[fetch.Query]: queries = list() for case in self.cases: for step in case.steps: @@ -297,7 +298,7 @@ def queries(self) -> List[fetch.Query]: return queries - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: ids = set() for case in self.cases: ids.update(case.participant_ids()) @@ -313,10 +314,10 @@ class ActionGeneratorReport(ImplicitDict): start_time: StringBasedDateTime """Time at which the action generator started""" - actions: List[TestSuiteActionReport] + actions: list[TestSuiteActionReport] """Reports from the actions generated by the action generator, in order of execution.""" - end_time: Optional[StringBasedDateTime] + end_time: StringBasedDateTime | None """Time at which the action generator completed or encountered an error""" successful: bool = False @@ -325,33 +326,33 @@ class ActionGeneratorReport(ImplicitDict): def has_critical_problem(self) -> bool: return any(a.has_critical_problem() for a in self.actions) - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: participants = set() for action in self.actions: participants = participants.union(action.all_participants()) return participants def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, action in enumerate(self.actions): for path, pc in action.query_passed_checks(participant_id): yield f"actions[{i}].{path}", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, action in enumerate(self.actions): for path, fc in action.query_failed_checks(participant_id): yield f"actions[{i}].{path}", fc - def queries(self) -> List[fetch.Query]: + def queries(self) -> list[fetch.Query]: queries = list() for action in self.actions: queries.extend(action.queries()) return queries - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: ids = set() for action in self.actions: ids.update(action.participant_ids()) @@ -359,19 +360,19 @@ def participant_ids(self) -> Set[ParticipantID]: class TestSuiteActionReport(ImplicitDict): - test_suite: Optional[TestSuiteReport] + test_suite: TestSuiteReport | None """If this action was a test suite, this field will hold its report""" - test_scenario: Optional[TestScenarioReport] + test_scenario: TestScenarioReport | None """If this action was a test scenario, this field will hold its report""" - action_generator: Optional[ActionGeneratorReport] + action_generator: ActionGeneratorReport | None """If this action was an action generator, this field will hold its report""" - skipped_action: Optional[SkippedActionReport] + skipped_action: SkippedActionReport | None """If this action was skipped, this field will hold its report""" - def get_applicable_report(self) -> Tuple[bool, bool, bool]: + def get_applicable_report(self) -> tuple[bool, bool, bool]: """Determine which type of report is applicable for this action. Note that skipped_action is applicable if none of the other return values are true. @@ -406,10 +407,10 @@ def get_applicable_report(self) -> Tuple[bool, bool, bool]: def _conditional( self, - test_suite_func: Union[Callable[[TestSuiteReport], Any], Callable[[Any], Any]], - test_scenario_func: Optional[Callable[[TestScenarioReport], Any]] = None, - action_generator_func: Optional[Callable[[ActionGeneratorReport], Any]] = None, - skipped_action_func: Optional[Callable[[SkippedActionReport], Any]] = None, + test_suite_func: Callable[[TestSuiteReport], Any] | Callable[[Any], Any], + test_scenario_func: Callable[[TestScenarioReport], Any] | None = None, + action_generator_func: Callable[[ActionGeneratorReport], Any] | None = None, + skipped_action_func: Callable[[SkippedActionReport], Any] | None = None, ) -> Any: test_suite, test_scenario, action_generator = self.get_applicable_report() if test_suite: @@ -435,12 +436,12 @@ def successful(self) -> bool: def has_critical_problem(self) -> bool: return self._conditional(lambda report: report.has_critical_problem()) - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: return self._conditional(lambda report: report.all_participants()) def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: test_suite, test_scenario, action_generator = self.get_applicable_report() if test_suite: report = self.test_suite @@ -458,8 +459,8 @@ def query_passed_checks( yield f"{prefix}.{path}", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, FailedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, FailedCheck]]: test_suite, test_scenario, action_generator = self.get_applicable_report() if test_suite: report = self.test_suite @@ -476,18 +477,18 @@ def query_failed_checks( for path, fc in report.query_failed_checks(participant_id): yield f"{prefix}.{path}", fc - def queries(self) -> List[fetch.Query]: + def queries(self) -> list[fetch.Query]: return self._conditional(lambda report: report.queries()) - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: return self._conditional(lambda report: report.participant_ids()) @property - def start_time(self) -> Optional[StringBasedDateTime]: + def start_time(self) -> StringBasedDateTime | None: return self._conditional(lambda report: report.start_time) @property - def end_time(self) -> Optional[StringBasedDateTime]: + def end_time(self) -> StringBasedDateTime | None: return self._conditional( lambda report: report.end_time if "end_time" in report else None ) @@ -496,27 +497,27 @@ def end_time(self) -> Optional[StringBasedDateTime]: class AllConditionsEvaluationReport(ImplicitDict): """Result of an evaluation of AllConditions determined by whether all the subconditions are satisfied.""" - satisfied_conditions: List[ParticipantCapabilityConditionEvaluationReport] + satisfied_conditions: list[ParticipantCapabilityConditionEvaluationReport] """All of the conditions that were satisfied (there must be at least one).""" - unsatisfied_conditions: List[ParticipantCapabilityConditionEvaluationReport] + unsatisfied_conditions: list[ParticipantCapabilityConditionEvaluationReport] """All of the conditions that were unsatisfied (if any, then this condition will not be satisfied).""" class AnyConditionEvaluationReport(ImplicitDict): """Result of an evaluation of AnyCondition determined by whether any of the subconditions are satisfied.""" - satisfied_options: List[ParticipantCapabilityConditionEvaluationReport] + satisfied_options: list[ParticipantCapabilityConditionEvaluationReport] """Which of the specified options were satisfied (if any were satisfied, then this condition should be satisfied).""" - unsatisfied_options: List[ParticipantCapabilityConditionEvaluationReport] + unsatisfied_options: list[ParticipantCapabilityConditionEvaluationReport] """Which of the specified options were not satisfied (these are informational only and do not affect the evaluation).""" class NoFailedChecksConditionEvaluationReport(ImplicitDict): """Result of an evaluation of NoFailedChecksCondition dependent on whether any checks failed within the scope of the test suite in which this condition is located.""" - failed_checks: List[JSONPathExpression] + failed_checks: list[JSONPathExpression] """The location of each FailedCheck, relative to the TestSuiteReport in which this report is located.""" @@ -526,23 +527,23 @@ class CheckedRequirement(ImplicitDict): requirement_id: RequirementID """The requirement being checked.""" - passed_checks: List[JSONPathExpression] + passed_checks: list[JSONPathExpression] """The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located.""" - failed_checks: List[JSONPathExpression] + failed_checks: list[JSONPathExpression] """The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located.""" class RequirementsCheckedConditionEvaluationReport(ImplicitDict): """Result of an evaluation of RequirementsCheckedCondition dependent on whether a set of requirements were successfully checked.""" - passed_requirements: List[CheckedRequirement] + passed_requirements: list[CheckedRequirement] """Requirements with only PassedChecks.""" - failed_requirements: List[CheckedRequirement] + failed_requirements: list[CheckedRequirement] """Requirements with FailedChecks.""" - untested_requirements: List[RequirementID] + untested_requirements: list[RequirementID] """Requirements that didn't have any PassedChecks or FailedChecks within the scope of the test suite in which this condition is located.""" @@ -575,13 +576,13 @@ class SpuriousReportMatch(ImplicitDict): class CapabilityVerifiedConditionEvaluationReport(ImplicitDict): """Result of an evaluation of a CapabilityVerifiedCondition dependent on whether other capabilities were verified.""" - checked_capabilities: List[CheckedCapability] + checked_capabilities: list[CheckedCapability] """All capability evaluations checked for this condition.""" - missing_capabilities: List[CapabilityID] + missing_capabilities: list[CapabilityID] """Capabilities specified for this condition but not found in the report.""" - spurious_matches: List[SpuriousReportMatch] + spurious_matches: list[SpuriousReportMatch] """Report elements matching the condition's `capability_location`, but not of the type TestSuiteReport.""" @@ -593,19 +594,19 @@ class ParticipantCapabilityConditionEvaluationReport(ImplicitDict): condition_satisfied: bool """Whether the condition was satisfied for the relevant participant.""" - all_conditions: Optional[AllConditionsEvaluationReport] + all_conditions: AllConditionsEvaluationReport | None """When specified, the condition evaluated was AllConditions.""" - any_conditions: Optional[AnyConditionEvaluationReport] + any_conditions: AnyConditionEvaluationReport | None """When specified, the condition evaluated was AnyCondition.""" - no_failed_checks: Optional[NoFailedChecksConditionEvaluationReport] + no_failed_checks: NoFailedChecksConditionEvaluationReport | None """When specified, the condition evaluated was NoFailedChecksCondition.""" - requirements_checked: Optional[RequirementsCheckedConditionEvaluationReport] + requirements_checked: RequirementsCheckedConditionEvaluationReport | None """When specified, the condition evaluated was RequirementsCheckedCondition.""" - capability_verified: Optional[CapabilityVerifiedConditionEvaluationReport] + capability_verified: CapabilityVerifiedConditionEvaluationReport | None """When specified, the condition evaluated was CapabilityVerifiedCondition.""" @@ -640,21 +641,21 @@ def successful(self) -> bool: def has_critical_problem(self) -> bool: return False - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: return set() - def queries(self) -> List[fetch.Query]: + def queries(self) -> list[fetch.Query]: return [] - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: return set() @property - def start_time(self) -> Optional[StringBasedDateTime]: + def start_time(self) -> StringBasedDateTime | None: return self.timestamp @property - def end_time(self) -> Optional[StringBasedDateTime]: + def end_time(self) -> StringBasedDateTime | None: return self.timestamp @@ -671,48 +672,48 @@ class TestSuiteReport(ImplicitDict): start_time: StringBasedDateTime """Time at which the test suite started""" - actions: List[TestSuiteActionReport] + actions: list[TestSuiteActionReport] """Reports from test scenarios and test suites comprising the test suite for this report, in order of execution.""" - end_time: Optional[StringBasedDateTime] + end_time: StringBasedDateTime | None """Time at which the test suite completed""" successful: bool = False """True iff test suite completed normally with no failed checks""" - capability_evaluations: List[ParticipantCapabilityEvaluationReport] + capability_evaluations: list[ParticipantCapabilityEvaluationReport] """List of capabilities defined in this test suite, evaluated for each participant.""" def has_critical_problem(self) -> bool: return any(a.has_critical_problem() for a in self.actions) - def all_participants(self) -> Set[ParticipantID]: + def all_participants(self) -> set[ParticipantID]: participants = set() for action in self.actions: participants = participants.union(action.all_participants()) return participants def query_passed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, PassedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: for i, action in enumerate(self.actions): for path, pc in action.query_passed_checks(participant_id): yield f"actions[{i}].{path}", pc def query_failed_checks( - self, participant_id: Optional[str] = None - ) -> Iterator[Tuple[JSONPathExpression, FailedCheck]]: + self, participant_id: str | None = None + ) -> Iterator[tuple[JSONPathExpression, FailedCheck]]: for i, action in enumerate(self.actions): for path, fc in action.query_failed_checks(participant_id): yield f"actions[{i}].{path}", fc - def queries(self) -> List[fetch.Query]: + def queries(self) -> list[fetch.Query]: queries = list() for action in self.actions: queries.extend(action.queries()) return queries - def participant_ids(self) -> Set[ParticipantID]: + def participant_ids(self) -> set[ParticipantID]: ids = set() for action in self.actions: ids.update(action.participant_ids()) @@ -742,11 +743,11 @@ class TestRunReport(ImplicitDict): report: TestSuiteActionReport """Report produced by configured test action""" - runtime_metadata: Optional[dict] + runtime_metadata: dict | None """Metadata for the test run specified at runtime.""" -def redact_access_tokens(report: Union[Dict[str, Any], list]) -> None: +def redact_access_tokens(report: dict[str, Any] | list) -> None: if isinstance(report, dict): changes = {} for k, v in report.items(): diff --git a/monitoring/uss_qualifier/reports/sequence_view/events.py b/monitoring/uss_qualifier/reports/sequence_view/events.py index e4aaae9fd1..7519e96d78 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/events.py +++ b/monitoring/uss_qualifier/reports/sequence_view/events.py @@ -3,7 +3,6 @@ import html import math from datetime import datetime -from typing import Dict, List, Optional, Tuple from implicitdict import ImplicitDict @@ -31,9 +30,9 @@ def _note_events( note_parent: ImplicitDict, indexer: Indexer, - after: Optional[datetime] = None, - before: Optional[datetime] = None, -) -> List[Event]: + after: datetime | None = None, + before: datetime | None = None, +) -> list[Event]: if "notes" not in note_parent or not note_parent.notes: return [] events = [] @@ -65,10 +64,10 @@ def _step_events( step: TestStepReport, note_parent: ImplicitDict, indexer: Indexer, - scenario_participants: Dict[ParticipantID, TestedParticipant], - all_events: List[Event], - after: Optional[datetime], -) -> Tuple[TestedStep, datetime]: + scenario_participants: dict[ParticipantID, TestedParticipant], + all_events: list[Event], + after: datetime | None, +) -> tuple[TestedStep, datetime]: events = [] # Create events for this step's passed checks @@ -190,7 +189,7 @@ def compute_tested_scenario( epochs = [] all_events = [] indexer = Indexer(index=1) - scenario_participants: Dict[ParticipantID, TestedParticipant] = {} + scenario_participants: dict[ParticipantID, TestedParticipant] = {} # Add any notes that occurred before the first test step latest_step_time = ( diff --git a/monitoring/uss_qualifier/reports/sequence_view/generate.py b/monitoring/uss_qualifier/reports/sequence_view/generate.py index a7bca2312b..720ae82e63 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/generate.py +++ b/monitoring/uss_qualifier/reports/sequence_view/generate.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from typing import Iterator, List +from collections.abc import Iterator from implicitdict import ImplicitDict from loguru import logger @@ -169,7 +169,7 @@ def _compute_overview_rows(node: ActionNode) -> Iterator[OverviewRow]: first_row = False -def _align_overview_rows(rows: List[OverviewRow]) -> None: +def _align_overview_rows(rows: list[OverviewRow]) -> None: max_suite_cols = max(len(r.suite_cells) for r in rows) to_fill = 0 for row in rows: @@ -210,7 +210,7 @@ def _align_overview_rows(rows: List[OverviewRow]) -> None: r0 += 1 -def _enumerate_all_participants(node: ActionNode) -> List[ParticipantID]: +def _enumerate_all_participants(node: ActionNode) -> list[ParticipantID]: if node.node_type == ActionNodeType.Scenario: return list(node.scenario.participants) else: diff --git a/monitoring/uss_qualifier/reports/sequence_view/kml.py b/monitoring/uss_qualifier/reports/sequence_view/kml.py index f696ea42a0..f21f2ac714 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/kml.py +++ b/monitoring/uss_qualifier/reports/sequence_view/kml.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Dict, List, Optional, Protocol, Type, get_type_hints +from typing import Protocol, get_type_hints from implicitdict import ImplicitDict from loguru import logger @@ -34,7 +34,7 @@ class QueryKMLRenderer(Protocol): def __call__( self, query: Query, req: ImplicitDict, resp: ImplicitDict - ) -> List[kml.Element]: + ) -> list[kml.Element]: """Function that renders the provided query information into KML elements. Args: @@ -47,14 +47,14 @@ def __call__( @dataclass -class QueryKMLRenderInfo(object): +class QueryKMLRenderInfo: renderer: QueryKMLRenderer include_query: bool - request_type: Optional[Type[ImplicitDict]] - response_type: Optional[Type[ImplicitDict]] + request_type: type[ImplicitDict] | None + response_type: type[ImplicitDict] | None -_query_kml_renderers: Dict[QueryType, QueryKMLRenderInfo] = {} +_query_kml_renderers: dict[QueryType, QueryKMLRenderInfo] = {} def query_kml_renderer(query_type: QueryType): diff --git a/monitoring/uss_qualifier/reports/sequence_view/summary_types.py b/monitoring/uss_qualifier/reports/sequence_view/summary_types.py index f189fd5f46..8a18cd71a4 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/summary_types.py +++ b/monitoring/uss_qualifier/reports/sequence_view/summary_types.py @@ -3,7 +3,6 @@ from dataclasses import dataclass from datetime import datetime from enum import Enum -from typing import Dict, List, Optional, Union from implicitdict import ImplicitDict @@ -33,11 +32,11 @@ class EventType(str, Enum): class Event(ImplicitDict): event_index: int = 0 - passed_check: Optional[PassedCheck] = None - failed_check: Optional[FailedCheck] = None - query_events: Optional[List[Union[Event, str]]] = None - query: Optional[Query] = None - note: Optional[NoteEvent] = None + passed_check: PassedCheck | None = None + failed_check: FailedCheck | None = None + query_events: list[Event | str] | None = None + query: Query | None = None + note: NoteEvent | None = None @property def type(self) -> EventType: @@ -78,7 +77,7 @@ def get_query_links(self) -> str: class TestedStep(ImplicitDict): name: str url: str - events: List[Event] + events: list[Event] @property def rows(self) -> int: @@ -88,7 +87,7 @@ def rows(self) -> int: class TestedCase(ImplicitDict): name: str url: str - steps: List[TestedStep] + steps: list[TestedStep] @property def rows(self) -> int: @@ -101,8 +100,8 @@ class EpochType(str, Enum): class Epoch(ImplicitDict): - case: Optional[TestedCase] = None - events: Optional[List[Event]] = None + case: TestedCase | None = None + events: list[Event] | None = None @property def type(self) -> EpochType: @@ -124,7 +123,7 @@ def rows(self) -> int: @dataclass -class TestedParticipant(object): +class TestedParticipant: has_failures: bool = False has_infos: bool = False has_successes: bool = False @@ -132,16 +131,16 @@ class TestedParticipant(object): @dataclass -class TestedScenario(object): +class TestedScenario: type: TestScenarioTypeName name: str url: str scenario_index: int duration: str - epochs: List[Epoch] - participants: Dict[ParticipantID, TestedParticipant] - execution_error: Optional[ErrorReport] - resource_origins: Dict[ResourceID, str] + epochs: list[Epoch] + participants: dict[ParticipantID, TestedParticipant] + execution_error: ErrorReport | None + resource_origins: dict[ResourceID, str] @property def rows(self) -> int: @@ -149,7 +148,7 @@ def rows(self) -> int: @dataclass -class SkippedAction(object): +class SkippedAction: reason: str @@ -163,9 +162,9 @@ class ActionNodeType(str, Enum): class ActionNode(ImplicitDict): name: str node_type: ActionNodeType - children: List[ActionNode] - scenario: Optional[TestedScenario] = None - skipped_action: Optional[SkippedAction] = None + children: list[ActionNode] + scenario: TestedScenario | None = None + skipped_action: SkippedAction | None = None @property def rows(self) -> int: @@ -177,21 +176,21 @@ def cols(self) -> int: @dataclass -class Indexer(object): +class Indexer: index: int = 1 @dataclass -class SuiteCell(object): - node: Optional[ActionNode] +class SuiteCell: + node: ActionNode | None first_row: bool rowspan: int = 1 colspan: int = 1 @dataclass -class OverviewRow(object): - suite_cells: List[SuiteCell] - scenario_node: Optional[ActionNode] = None - skipped_action_node: Optional[ActionNode] = None +class OverviewRow: + suite_cells: list[SuiteCell] + scenario_node: ActionNode | None = None + skipped_action_node: ActionNode | None = None filled: bool = False diff --git a/monitoring/uss_qualifier/reports/templates.py b/monitoring/uss_qualifier/reports/templates.py index cb3caed04f..d7bafe3a35 100644 --- a/monitoring/uss_qualifier/reports/templates.py +++ b/monitoring/uss_qualifier/reports/templates.py @@ -4,7 +4,6 @@ import os import pathlib import zipfile -from typing import List import requests from loguru import logger @@ -84,7 +83,7 @@ def render(self, base_path: str): def render_templates( base_path: str, - templated_reports: List[TemplatedReportConfiguration], + templated_reports: list[TemplatedReportConfiguration], report: TestRunReport, ): pathlib.Path(CACHE_TEMPLATE_PATH).mkdir(parents=True, exist_ok=True) diff --git a/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py b/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py index 7c72f89815..4a7a359405 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py @@ -1,4 +1,4 @@ -from typing import Iterable, List, Optional, Set, Tuple, Union +from collections.abc import Iterable from implicitdict import ImplicitDict @@ -44,7 +44,7 @@ def make_breakdown( report: TestRunReport, - participant_reqs: Optional[Set[RequirementID]], + participant_reqs: set[RequirementID] | None, participant_ids: Iterable[ParticipantID], ) -> TestedBreakdown: """Break down a report into requirements tested for the specified participants. @@ -70,7 +70,7 @@ def make_breakdown( def _populate_breakdown_with_req_set( - breakdown: TestedBreakdown, req_set: Set[RequirementID] + breakdown: TestedBreakdown, req_set: set[RequirementID] ) -> None: for req_id in req_set: package_id = req_id.package() @@ -97,7 +97,7 @@ def _populate_breakdown_with_action_report( breakdown: TestedBreakdown, action: TestSuiteActionReport, participant_ids: Iterable[ParticipantID], - req_set: Optional[Set[RequirementID]], + req_set: set[RequirementID] | None, ) -> None: test_suite, test_scenario, action_generator = action.get_applicable_report() if test_scenario: @@ -122,10 +122,10 @@ def _populate_breakdown_with_scenario_report( breakdown: TestedBreakdown, scenario_report: TestScenarioReport, participant_ids: Iterable[ParticipantID], - req_set: Optional[Set[RequirementID]], + req_set: set[RequirementID] | None, ) -> None: scenario_type_name = scenario_report.scenario_type - steps: List[Tuple[Optional[TestCaseReport], TestStepReport]] = [] + steps: list[tuple[TestCaseReport | None, TestStepReport]] = [] for case in scenario_report.cases: for step in case.steps: steps.append((case, step)) @@ -225,8 +225,8 @@ def _populate_breakdown_with_scenario_report( def _populate_breakdown_with_action_declaration( breakdown: TestedBreakdown, - action: Union[TestSuiteActionDeclaration, PotentialGeneratedAction], - req_set: Optional[Set[RequirementID]], + action: TestSuiteActionDeclaration | PotentialGeneratedAction, + req_set: set[RequirementID] | None, ) -> None: action_type = action.get_action_type() if action_type == ActionType.TestScenario: @@ -262,7 +262,7 @@ def _populate_breakdown_with_action_declaration( def _populate_breakdown_with_scenario( breakdown: TestedBreakdown, scenario_type_name: TestScenarioTypeName, - req_set: Optional[Set[RequirementID]], + req_set: set[RequirementID] | None, ) -> None: scenario_type = get_scenario_type_by_name(scenario_type_name) scenario_doc = get_documentation(scenario_type) diff --git a/monitoring/uss_qualifier/reports/tested_requirements/data_types.py b/monitoring/uss_qualifier/reports/tested_requirements/data_types.py index a224c12e52..ac0c4a9739 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/data_types.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/data_types.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import Dict, List, Optional from implicitdict import ImplicitDict @@ -64,7 +63,7 @@ def not_tested(self) -> bool: class TestedStep(ImplicitDict): name: str url: str - checks: List[TestedCheck] + checks: list[TestedCheck] @property def rows(self) -> int: @@ -86,7 +85,7 @@ def findings(self) -> bool: class TestedCase(ImplicitDict): name: str url: str - steps: List[TestedStep] + steps: list[TestedStep] @property def rows(self) -> int: @@ -109,7 +108,7 @@ class TestedScenario(ImplicitDict): type: TestScenarioTypeName name: str url: str - cases: List[TestedCase] + cases: list[TestedCase] @property def rows(self) -> int: @@ -130,7 +129,7 @@ def findings(self) -> bool: class TestedRequirement(ImplicitDict): id: str - scenarios: List[TestedScenario] + scenarios: list[TestedScenario] @property def rows(self) -> int: @@ -155,7 +154,7 @@ class TestedPackage(ImplicitDict): id: PackageID url: str name: str - requirements: List[TestedRequirement] + requirements: list[TestedRequirement] @property def rows(self) -> int: @@ -163,13 +162,13 @@ def rows(self) -> int: class TestedBreakdown(ImplicitDict): - packages: List[TestedPackage] + packages: list[TestedPackage] class TestRunInformation(ImplicitDict): test_run_id: str - start_time: Optional[str] = None - end_time: Optional[str] = None + start_time: str | None = None + end_time: str | None = None baseline: str environment: str @@ -207,7 +206,7 @@ class ParticipantVerificationInfo(ImplicitDict): status: ParticipantVerificationStatus """Verification status of participant for the associated requirements set.""" - system_version: Optional[str] = None + system_version: str | None = None """The version of the participant's system that was tested, if this information was acquired during testing.""" @@ -215,9 +214,9 @@ class RequirementsVerificationReport(ImplicitDict): test_run_information: TestRunInformation """Information about the test run during which the participant_verifications were determined.""" - participant_verifications: Dict[ParticipantID, ParticipantVerificationInfo] + participant_verifications: dict[ParticipantID, ParticipantVerificationInfo] """Information regarding verification of compliance for each participant.""" - artifact_configuration: Optional[str] + artifact_configuration: str | None """Name of the tested requirements artifact configuration from the test run configuration, or "post-hoc" if the artifact configuration generating this verification report is not specified in the test run configuration.""" diff --git a/monitoring/uss_qualifier/reports/tested_requirements/generate.py b/monitoring/uss_qualifier/reports/tested_requirements/generate.py index 0050ed69d8..b1ed75d1f3 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/generate.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/generate.py @@ -1,6 +1,5 @@ import json import os -from typing import Dict, Optional, Set from monitoring.monitorlib.inspection import import_submodules from monitoring.monitorlib.versioning import get_code_version @@ -59,8 +58,8 @@ def generate_tested_requirements( config_source = "post-hoc artifact configuration" artifact_configuration = "post-hoc" - req_collections: Dict[ - TestedRequirementsCollectionIdentifier, Set[RequirementID] + req_collections: dict[ + TestedRequirementsCollectionIdentifier, set[RequirementID] ] = {} if "requirement_collections" in config and config.requirement_collections: req_collections = { @@ -68,8 +67,8 @@ def generate_tested_requirements( for k, v in config.requirement_collections.items() } - participant_req_collections: Dict[ParticipantID, Optional[Set[RequirementID]]] = {} - participant_req_set_names: Dict[ParticipantID, str] = {} + participant_req_collections: dict[ParticipantID, set[RequirementID] | None] = {} + participant_req_set_names: dict[ParticipantID, str] = {} if "participant_requirements" in config and config.participant_requirements: for k, v in config.participant_requirements.items(): if v and v not in req_collections: diff --git a/monitoring/uss_qualifier/reports/tested_requirements/sorting.py b/monitoring/uss_qualifier/reports/tested_requirements/sorting.py old mode 100644 new mode 100755 index 9a9a34ed01..4b5c85eeb6 --- a/monitoring/uss_qualifier/reports/tested_requirements/sorting.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/sorting.py @@ -1,5 +1,4 @@ from functools import cmp_to_key -from typing import List, Union from monitoring.uss_qualifier.reports.tested_requirements.data_types import ( TestedBreakdown, @@ -7,7 +6,7 @@ ) -def _split_strings_numbers(s: str) -> List[Union[int, str]]: +def _split_strings_numbers(s: str) -> list[int | str]: digits = "0123456789" current_number = "" current_string = "" @@ -30,7 +29,7 @@ def _split_strings_numbers(s: str) -> List[Union[int, str]]: return parts -def _requirement_id_parts(req_id: str) -> List[str]: +def _requirement_id_parts(req_id: str) -> list[str]: """Split a requirement ID into sortable parts. Each ID is split into parts in multiple phases (example: astm.f3411.v22a.NET0260,Table1,1b): diff --git a/monitoring/uss_qualifier/reports/tested_requirements/summaries.py b/monitoring/uss_qualifier/reports/tested_requirements/summaries.py index dab09c9c43..368f2aa9c0 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/summaries.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/summaries.py @@ -1,4 +1,4 @@ -from typing import Iterable, List, Optional, Union +from collections.abc import Iterable from implicitdict import StringBasedDateTime @@ -17,7 +17,7 @@ def compute_test_run_information(report: TestRunReport) -> TestRunInformation: - def print_datetime(t: Optional[StringBasedDateTime]) -> Optional[str]: + def print_datetime(t: StringBasedDateTime | None) -> str | None: if t is None: return None return t.datetime.strftime("%Y-%m-%d %H:%M:%S %Z") @@ -53,8 +53,8 @@ def compute_overall_status( def find_participant_system_versions( report: TestSuiteActionReport, - participant_ids: Union[ParticipantID, Iterable[ParticipantID]], -) -> List[str]: + participant_ids: ParticipantID | Iterable[ParticipantID], +) -> list[str]: if isinstance(participant_ids, ParticipantID): participant_ids = [participant_ids] test_suite, test_scenario, action_generator = report.get_applicable_report() @@ -83,7 +83,7 @@ def find_participant_system_versions( return result -def get_system_version(system_versions: List[str]) -> Optional[str]: +def get_system_version(system_versions: list[str]) -> str | None: if not system_versions: return None elif len(system_versions) > 1 and any( diff --git a/monitoring/uss_qualifier/reports/validation/definitions.py b/monitoring/uss_qualifier/reports/validation/definitions.py index 77237bb6d9..b3d0e05ae7 100644 --- a/monitoring/uss_qualifier/reports/validation/definitions.py +++ b/monitoring/uss_qualifier/reports/validation/definitions.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import List, Optional - from implicitdict import ImplicitDict from monitoring.monitorlib.dicts import JSONAddress @@ -13,21 +11,21 @@ class SeverityComparison(ImplicitDict): """Exactly one field must be specified.""" - equal_to: Optional[Severity] - at_least: Optional[Severity] - higher_than: Optional[Severity] - no_higher_than: Optional[Severity] - lower_than: Optional[Severity] + equal_to: Severity | None + at_least: Severity | None + higher_than: Severity | None + no_higher_than: Severity | None + lower_than: Severity | None class NumericComparison(ImplicitDict): """Exactly one field must be specified.""" - equal_to: Optional[float] - at_least: Optional[float] - more_than: Optional[float] - no_more_than: Optional[float] - less_than: Optional[float] + equal_to: float | None + at_least: float | None + more_than: float | None + no_more_than: float | None + less_than: float | None # ===== Applicability ===== @@ -42,7 +40,7 @@ class TestScenarioApplicability(ImplicitDict): class FailedCheckApplicability(ImplicitDict): """FailedCheck test report elements are applicable according to this specification.""" - has_severity: Optional[SeverityComparison] + has_severity: SeverityComparison | None """If specified, only FailedChecks with specified severity are applicable.""" @@ -55,14 +53,14 @@ class SkippedCheckApplicability(ImplicitDict): class AllCriteriaApplicability(ImplicitDict): """All criteria must be met for an element to be applicable.""" - criteria: List[ValidationCriterionApplicability] + criteria: list[ValidationCriterionApplicability] """Criteria that must all be met.""" class AnyCriteriaApplicability(ImplicitDict): """Any criterion or criteria must be met for an element to be applicable.""" - criteria: List[ValidationCriterionApplicability] + criteria: list[ValidationCriterionApplicability] """Options for criterion/criteria to meet.""" @@ -71,25 +69,25 @@ class ValidationCriterionApplicability(ImplicitDict): Exactly one field must be specified.""" - test_scenarios: Optional[TestScenarioApplicability] + test_scenarios: TestScenarioApplicability | None """Only this kind of TestScenarioReport elements are applicable.""" - failed_checks: Optional[FailedCheckApplicability] + failed_checks: FailedCheckApplicability | None """Only this kind of FailedCheck elements are applicable.""" - skipped_actions: Optional[SkippedCheckApplicability] + skipped_actions: SkippedCheckApplicability | None """Only this kind of SkippedCheckReport elements are applicable.""" - address_is: Optional[JSONAddress] + address_is: JSONAddress | None """Only the element at this JSONAddress in the test report is applicable.""" - does_not_satisfy: Optional[ValidationCriterionApplicability] + does_not_satisfy: ValidationCriterionApplicability | None """Only elements that do not satisfy this criterion are applicable.""" - satisfies_all: Optional[AllCriteriaApplicability] + satisfies_all: AllCriteriaApplicability | None """Only elements which satisfy all these criteria are applicable.""" - satisfies_any: Optional[AnyCriteriaApplicability] + satisfies_any: AnyCriteriaApplicability | None """Elements which satisfy any of these criteria are applicable.""" @@ -99,50 +97,50 @@ class ValidationCriterionApplicability(ImplicitDict): class EachElementCondition(ImplicitDict): """A single applicable element must meet this condition. Exactly one field must be specified.""" - has_severity: Optional[SeverityComparison] + has_severity: SeverityComparison | None """The element must be a FailedCheck that has this specified kind of severity.""" - has_execution_error: Optional[bool] + has_execution_error: bool | None """The element must be a TestScenarioReport that either must have or must not have an execution error.""" class ElementGroupCondition(ImplicitDict): """A group of applicable elements must meet this condition. Exactly one field must be specified.""" - count: Optional[NumericComparison] + count: NumericComparison | None """The number of applicable elements must have this specified kind of count.""" class AllPassConditions(ImplicitDict): """All specific conditions must be met.""" - conditions: List[PassCondition] + conditions: list[PassCondition] """Conditions that all must be met.""" class AnyPassCondition(ImplicitDict): """Any specific condition must be met.""" - conditions: List[PassCondition] + conditions: list[PassCondition] """Options for conditions to meet.""" class PassCondition(ImplicitDict): """Condition for passing validation. Exactly one field must be specified.""" - each_element: Optional[EachElementCondition] + each_element: EachElementCondition | None """Condition applies to each applicable element.""" - elements: Optional[ElementGroupCondition] + elements: ElementGroupCondition | None """Condition applies to the group of applicable elements.""" - does_not_pass: Optional[PassCondition] + does_not_pass: PassCondition | None """Overall condition is met only if this specified condition is not met.""" - all_of: Optional[AllPassConditions] + all_of: AllPassConditions | None """Overall condition is met only if all of these specified conditions are met.""" - any_of: Optional[AnyPassCondition] + any_of: AnyPassCondition | None """Overall condition is met if any of these specified conditions are met.""" @@ -162,5 +160,5 @@ class ValidationCriterion(ImplicitDict): class ValidationConfiguration(ImplicitDict): """Complete set of validation criteria that a test run report must satisfy.""" - criteria: List[ValidationCriterion] + criteria: list[ValidationCriterion] """Set of criteria which must all pass in order to pass validation.""" diff --git a/monitoring/uss_qualifier/reports/validation/report_validation.py b/monitoring/uss_qualifier/reports/validation/report_validation.py index 6978bb2faa..a25d59817a 100644 --- a/monitoring/uss_qualifier/reports/validation/report_validation.py +++ b/monitoring/uss_qualifier/reports/validation/report_validation.py @@ -1,6 +1,6 @@ import json +from collections.abc import Iterator from dataclasses import dataclass -from typing import Iterator, List, Union import yaml from loguru import logger @@ -32,8 +32,8 @@ @dataclass -class TestReportElement(object): - element: Union[FailedCheck, SkippedActionReport, TestScenarioReport] +class TestReportElement: + element: FailedCheck | SkippedActionReport | TestScenarioReport location: JSONAddress @@ -145,10 +145,9 @@ def _get_applicable_elements_from_test_suite( location: JSONAddress, ) -> Iterator[TestReportElement]: for a, action in enumerate(report.actions): - for e in _get_applicable_elements_from_action( + yield from _get_applicable_elements_from_action( applicability, action, JSONAddress(location + f".actions[{a}]") - ): - yield e + ) def _get_applicable_elements_from_action_generator( @@ -157,10 +156,9 @@ def _get_applicable_elements_from_action_generator( location: JSONAddress, ) -> Iterator[TestReportElement]: for a, action in enumerate(report.actions): - for e in _get_applicable_elements_from_action( + yield from _get_applicable_elements_from_action( applicability, action, JSONAddress(location + f".actions[{a}]") - ): - yield e + ) def _get_applicable_elements_from_skipped_action( @@ -237,7 +235,7 @@ def _evaluate_element_condition( def _evaluate_elements_condition( - condition: ElementGroupCondition, elements: List[TestReportElement] + condition: ElementGroupCondition, elements: list[TestReportElement] ) -> bool: if "count" in condition and condition.count is not None: return _compare_number(len(elements), condition.count) @@ -249,7 +247,7 @@ def _evaluate_elements_condition( def _evaluate_condition( - condition: PassCondition, elements: List[TestReportElement] + condition: PassCondition, elements: list[TestReportElement] ) -> bool: if "each_element" in condition and condition.each_element is not None: for element in elements: diff --git a/monitoring/uss_qualifier/requirements/definitions.py b/monitoring/uss_qualifier/requirements/definitions.py index 71396b6128..3d320de30b 100644 --- a/monitoring/uss_qualifier/requirements/definitions.py +++ b/monitoring/uss_qualifier/requirements/definitions.py @@ -1,7 +1,6 @@ from __future__ import annotations import os -from typing import List, Optional from implicitdict import ImplicitDict @@ -121,14 +120,14 @@ def anchor(self) -> str: class RequirementCollection(ImplicitDict): - requirements: Optional[List[RequirementID]] + requirements: list[RequirementID] | None """This collection includes all of these requirements.""" - requirement_sets: Optional[List[RequirementSetID]] + requirement_sets: list[RequirementSetID] | None """This collection includes all requirements in all of these requirement sets.""" - requirement_collections: Optional[List[RequirementCollection]] + requirement_collections: list[RequirementCollection] | None """This collection includes all of the requirements in all of these requirement collections.""" - exclude: Optional[RequirementCollection] + exclude: RequirementCollection | None """This collection does not include any of these requirements, despite all previous fields.""" diff --git a/monitoring/uss_qualifier/requirements/documentation.py b/monitoring/uss_qualifier/requirements/documentation.py index bc62c2d8fc..2df0cdcf7f 100644 --- a/monitoring/uss_qualifier/requirements/documentation.py +++ b/monitoring/uss_qualifier/requirements/documentation.py @@ -1,5 +1,4 @@ import os -from typing import Dict, List, Set import marko import marko.element @@ -15,12 +14,12 @@ ) -class Requirement(object): +class Requirement: def __init__(self, requirement_id: RequirementID): self.requirement_id = requirement_id -_verified_requirements: Set[RequirementID] = set() +_verified_requirements: set[RequirementID] = set() def _verify_requirements(parent: marko.element.Element, package: PackageID) -> None: @@ -47,7 +46,7 @@ def _load_requirement(requirement_id: RequirementID) -> None: raise ValueError( f'Could not load requirement "{requirement_id}" because the file "{md_filename}" does not exist' ) - with open(md_filename, "r") as f: + with open(md_filename) as f: doc = marko.parse(f.read()) _verify_requirements(doc, requirement_id.package()) if requirement_id not in _verified_requirements: @@ -64,13 +63,13 @@ def get_requirement(requirement_id: RequirementID) -> Requirement: class RequirementSet(ImplicitDict): name: str - requirement_ids: List[RequirementID] + requirement_ids: list[RequirementID] REQUIREMENT_SET_SUFFIX = " requirement set" -_requirement_sets: Dict[RequirementSetID, RequirementSet] = {} +_requirement_sets: dict[RequirementSetID, RequirementSet] = {} def _length_of_section(values, start_of_section: int) -> int: @@ -95,7 +94,7 @@ def _find_section(values, section_title: str) -> int: def _parse_requirements( parent: marko.element.Element, start_index: int = 0, end_index: int = 0 -) -> List[RequirementID]: +) -> list[RequirementID]: reqs = [] if hasattr(parent, "children") and not isinstance(parent.children, str): if end_index <= start_index: @@ -121,7 +120,7 @@ def _load_requirement_set(requirement_set_id: RequirementSetID) -> RequirementSe raise ValueError( f'Could not load requirement set "{requirement_set_id}" because the file "{md_filename}" does not exist' ) - with open(md_filename, "r") as f: + with open(md_filename) as f: doc = marko.parse(f.read()) # Extract the file-level name from the first top-level header @@ -169,7 +168,7 @@ def get_requirement_set(requirement_set_id: RequirementSetID) -> RequirementSet: def resolve_requirements_collection( collection: RequirementCollection, -) -> Set[RequirementID]: +) -> set[RequirementID]: """Compute the set of requirement IDs identified by the specified requirements collection. Args: @@ -177,7 +176,7 @@ def resolve_requirements_collection( Returns: Set of IDs of requirements identified by the specified collection. """ - reqs: Set[RequirementID] = set() + reqs: set[RequirementID] = set() if "requirements" in collection and collection.requirements: for req_id in collection.requirements: diff --git a/monitoring/uss_qualifier/resources/astm/f3411/dss.py b/monitoring/uss_qualifier/resources/astm/f3411/dss.py index 2c77f5d378..ac6e59eb07 100644 --- a/monitoring/uss_qualifier/resources/astm/f3411/dss.py +++ b/monitoring/uss_qualifier/resources/astm/f3411/dss.py @@ -1,6 +1,5 @@ from __future__ import annotations -from typing import List from urllib.parse import urlparse from implicitdict import ImplicitDict @@ -30,7 +29,7 @@ def __init__(self, *args, **kwargs): raise ValueError("DSSInstanceConfiguration.base_url must be a URL") -class DSSInstance(object): +class DSSInstance: participant_id: ParticipantID rid_version: RIDVersion base_url: str @@ -65,7 +64,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(DSSInstanceResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) # Note that the current implementation does not support acting as just a # SP accessing the DSS or just a DP accessing the DSS, but this could be @@ -96,11 +95,11 @@ def from_dss_instance( class DSSInstancesSpecification(ImplicitDict): - dss_instances: List[DSSInstanceSpecification] + dss_instances: list[DSSInstanceSpecification] class DSSInstancesResource(Resource[DSSInstancesSpecification]): - dss_instances: List[DSSInstance] + dss_instances: list[DSSInstance] def __init__( self, @@ -108,7 +107,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(DSSInstancesResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.dss_instances = [ DSSInstanceResource( s, f"instance {i + 1} in {resource_origin}", auth_adapter diff --git a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py index d0e9b82a80..8cec598457 100644 --- a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py +++ b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py @@ -3,7 +3,6 @@ import datetime import uuid from enum import Enum -from typing import Dict, List, Optional, Set, Tuple from urllib.parse import urlparse import s2sphere @@ -57,13 +56,13 @@ class DSSInstanceSpecification(ImplicitDict): participant_id: str """ID of the USS responsible for this DSS instance""" - user_participant_ids: Optional[List[str]] + user_participant_ids: list[str] | None """IDs of any participants using this DSS instance, apart from the USS responsible for this DSS instance.""" base_url: str """Base URL for the DSS instance according to the ASTM F3548-21 API""" - supports_ovn_request: Optional[bool] + supports_ovn_request: bool | None """Whether this DSS instance supports the optional extension not part of the original F3548 standard API allowing a USS to request a specific OVN when creating or updating an operational intent.""" def __init__(self, *args, **kwargs): @@ -74,20 +73,20 @@ def __init__(self, *args, **kwargs): raise ValueError("DSSInstanceConfiguration.base_url must be a URL") -class DSSInstance(object): +class DSSInstance: participant_id: str - user_participant_ids: List[str] + user_participant_ids: list[str] base_url: str client: infrastructure.UTMClientSession - _scopes_authorized: Set[str] + _scopes_authorized: set[str] def __init__( self, participant_id: str, - user_participant_ids: List[str], + user_participant_ids: list[str], base_url: str, auth_adapter: infrastructure.AuthAdapter, - scopes_authorized: List[str], + scopes_authorized: list[str], ): self.participant_id = participant_id self.user_participant_ids = user_participant_ids @@ -97,7 +96,7 @@ def __init__( s.value if isinstance(s, Enum) else s for s in scopes_authorized ) - def _uses_scope(self, *scopes: Tuple[str]) -> None: + def _uses_scope(self, *scopes: tuple[str]) -> None: for scope in scopes: if scope not in self._scopes_authorized: raise ValueError( @@ -118,7 +117,7 @@ def can_use_scope(self, scope: str) -> bool: return scope in self._scopes_authorized def with_different_auth( - self, auth_adapter: AuthAdapterResource, scopes_required: Dict[str, str] + self, auth_adapter: AuthAdapterResource, scopes_required: dict[str, str] ) -> DSSInstance: auth_adapter.assert_scopes_available( scopes_required, "DSSInstance.with_different_auth" @@ -133,7 +132,7 @@ def with_different_auth( def find_op_intent( self, extent: Volume4D - ) -> Tuple[List[OperationalIntentReference], Query]: + ) -> tuple[list[OperationalIntentReference], Query]: """ Find operational intents overlapping with a given volume 4D. Raises: @@ -163,7 +162,7 @@ def find_op_intent( def get_op_intent_reference( self, op_intent_id: str, - ) -> Tuple[OperationalIntentReference, Query]: + ) -> tuple[OperationalIntentReference, Query]: """ Retrieve an OP Intent from the DSS, using only its ID Raises: @@ -191,8 +190,8 @@ def get_op_intent_reference( def get_full_op_intent( self, op_intent_ref: OperationalIntentReference, - uss_participant_id: Optional[str] = None, - ) -> Tuple[OperationalIntent, Query]: + uss_participant_id: str | None = None, + ) -> tuple[OperationalIntent, Query]: """ Retrieve a full operational intent from its managing USS. Raises: @@ -220,8 +219,8 @@ def get_full_op_intent( def get_op_intent_telemetry( self, op_intent_ref: OperationalIntentReference, - uss_participant_id: Optional[str] = None, - ) -> Tuple[Optional[VehicleTelemetry], Query]: + uss_participant_id: str | None = None, + ) -> tuple[VehicleTelemetry | None, Query]: """ Get telemetry of an operational intent. Returns: @@ -252,19 +251,19 @@ def get_op_intent_telemetry( def put_op_intent( self, - extents: List[Volume4D], - key: List[EntityOVN], + extents: list[Volume4D], + key: list[EntityOVN], state: OperationalIntentState, base_url: UssBaseURL, - oi_id: Optional[str] = None, - ovn: Optional[str] = None, - subscription_id: Optional[str] = None, - force_query_scopes: Optional[Scope] = None, + oi_id: str | None = None, + ovn: str | None = None, + subscription_id: str | None = None, + force_query_scopes: Scope | None = None, force_no_implicit_subscription: bool = False, - requested_ovn_suffix: Optional[UUIDv7Format] = None, - ) -> Tuple[ + requested_ovn_suffix: UUIDv7Format | None = None, + ) -> tuple[ OperationalIntentReference, - List[SubscriberToNotify], + list[SubscriberToNotify], Query, ]: """ @@ -357,7 +356,7 @@ def delete_op_intent( self, id: str, ovn: str, - ) -> Tuple[OperationalIntentReference, List[SubscriberToNotify], Query]: + ) -> tuple[OperationalIntentReference, list[SubscriberToNotify], Query]: """ Delete an operational intent. Raises: @@ -386,7 +385,7 @@ def get_uss_availability( self, uss_id: str, scope: Scope = Scope.StrategicCoordination, - ) -> Tuple[UssAvailabilityStatusResponse, Query]: + ) -> tuple[UssAvailabilityStatusResponse, Query]: """ Request the availability status for the specified USS. @@ -417,9 +416,9 @@ def get_uss_availability( def set_uss_availability( self, uss_id: str, - available: Optional[bool], + available: bool | None, version: str = "", - ) -> Tuple[str, Query]: + ) -> tuple[str, Query]: """ Set the availability for the USS identified by 'uss_id'. @@ -467,10 +466,10 @@ def set_uss_availability( def put_constraint_ref( self, cr_id: str, - extents: List[Volume4D], + extents: list[Volume4D], uss_base_url: UssBaseURL, - ovn: Optional[str] = None, - ) -> Tuple[ConstraintReference, List[SubscriberToNotify], Query]: + ovn: str | None = None, + ) -> tuple[ConstraintReference, list[SubscriberToNotify], Query]: """ Create or update a constraint reference. Returns: @@ -514,7 +513,7 @@ def put_constraint_ref( query, ) - def get_constraint_ref(self, id: str) -> Tuple[ConstraintReference, Query]: + def get_constraint_ref(self, id: str) -> tuple[ConstraintReference, Query]: """ Retrieve a constraint reference from the DSS, using only its ID Raises: @@ -541,7 +540,7 @@ def get_constraint_ref(self, id: str) -> Tuple[ConstraintReference, Query]: def find_constraint_ref( self, extent: Volume4D - ) -> Tuple[List[ConstraintReference], Query]: + ) -> tuple[list[ConstraintReference], Query]: """ Find constraint references overlapping with a given volume 4D. Raises: @@ -572,7 +571,7 @@ def delete_constraint_ref( self, id: str, ovn: str, - ) -> Tuple[ConstraintReference, List[SubscriberToNotify], Query]: + ) -> tuple[ConstraintReference, list[SubscriberToNotify], Query]: """ Delete a constraint reference. Raises: @@ -600,7 +599,7 @@ def delete_constraint_ref( def make_report( self, exchange: ExchangeRecord, - ) -> Tuple[Optional[str], Query]: + ) -> tuple[str | None, Query]: """ Make a DSS report. Returns: @@ -662,7 +661,7 @@ def upsert_subscription( notify_for_constraints: bool, min_alt_m: float, max_alt_m: float, - version: Optional[str] = None, + version: str | None = None, ) -> MutatedSubscription: self._uses_scope(Scope.StrategicCoordination) return mutate.upsert_subscription( @@ -712,14 +711,14 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(DSSInstanceResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self._specification = specification self._auth_adapter = auth_adapter def can_use_scope(self, scope: str) -> bool: return scope in self._auth_adapter.scopes - def get_authorized_scopes(self) -> Set[str]: + def get_authorized_scopes(self) -> set[str]: return self._auth_adapter.scopes.copy() @property @@ -739,7 +738,7 @@ def supports_ovn_request(self) -> bool: else False ) - def get_authorized_scope_not_in(self, ignored_scopes: List[str]) -> Optional[Scope]: + def get_authorized_scope_not_in(self, ignored_scopes: list[str]) -> Scope | None: """Returns a scope that this DSS Resource is allowed to use but that is not any of the ones that are passed in 'ignored_scopes'. If no such scope is found, None is returned. @@ -756,7 +755,7 @@ def get_authorized_scope_not_in(self, ignored_scopes: List[str]) -> Optional[Sco return None - def get_instance(self, scopes_required: Dict[str, str]) -> DSSInstance: + def get_instance(self, scopes_required: dict[str, str]) -> DSSInstance: """Get a client object ready to be used. This method should generally be called in the constructor of a test @@ -800,11 +799,11 @@ def is_same_as(self, other: DSSInstanceResource) -> bool: class DSSInstancesSpecification(ImplicitDict): - dss_instances: List[DSSInstanceSpecification] + dss_instances: list[DSSInstanceSpecification] class DSSInstancesResource(Resource[DSSInstancesSpecification]): - dss_instances: List[DSSInstanceResource] + dss_instances: list[DSSInstanceResource] def __init__( self, @@ -812,7 +811,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(DSSInstancesResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.dss_instances = [ DSSInstanceResource( specification=s, diff --git a/monitoring/uss_qualifier/resources/communications/auth_adapter.py b/monitoring/uss_qualifier/resources/communications/auth_adapter.py index 74b0b48d55..615db07ca0 100644 --- a/monitoring/uss_qualifier/resources/communications/auth_adapter.py +++ b/monitoring/uss_qualifier/resources/communications/auth_adapter.py @@ -1,6 +1,5 @@ import os from enum import Enum -from typing import Dict, List, Optional, Set from implicitdict import ImplicitDict @@ -17,22 +16,22 @@ class AuthAdapterSpecification(ImplicitDict): * environment_variable_containing_auth_spec """ - auth_spec: Optional[str] + auth_spec: str | None """Literal representation of auth spec. WARNING: Specifying this directly may cause sensitive information to be included in reports and unprotected configuration files.""" - environment_variable_containing_auth_spec: Optional[str] + environment_variable_containing_auth_spec: str | None """Name of environment variable containing the auth spec. This is the preferred method of providing the auth spec.""" - scopes_authorized: List[str] + scopes_authorized: list[str] """List of scopes the user in the auth spec is authorized to obtain.""" class AuthAdapterResource(Resource[AuthAdapterSpecification]): adapter: infrastructure.AuthAdapter - scopes: Set[str] + scopes: set[str] def __init__(self, specification: AuthAdapterSpecification, resource_origin: str): - super(AuthAdapterResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) if ( "environment_variable_containing_auth_spec" in specification and specification.environment_variable_containing_auth_spec @@ -42,9 +41,7 @@ def __init__(self, specification: AuthAdapterSpecification, resource_origin: str not in os.environ ): raise ValueError( - "Environment variable {} could not be found".format( - specification.environment_variable_containing_auth_spec - ) + f"Environment variable {specification.environment_variable_containing_auth_spec} could not be found" ) spec = os.environ[specification.environment_variable_containing_auth_spec] elif "auth_spec" in specification and specification.auth_spec: @@ -55,7 +52,7 @@ def __init__(self, specification: AuthAdapterSpecification, resource_origin: str self.scopes = set(specification.scopes_authorized) def assert_scopes_available( - self, scopes_required: Dict[str, str], consumer_name: str + self, scopes_required: dict[str, str], consumer_name: str ) -> None: """Raise a MissingResourceError if any of the scopes_required are not available. diff --git a/monitoring/uss_qualifier/resources/communications/client_identity.py b/monitoring/uss_qualifier/resources/communications/client_identity.py index cd18936cfd..51975995af 100644 --- a/monitoring/uss_qualifier/resources/communications/client_identity.py +++ b/monitoring/uss_qualifier/resources/communications/client_identity.py @@ -36,7 +36,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(ClientIdentityResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.specification = specification # Keep the adapter: we will only use it later at the moment it is required self._adapter = auth_adapter.adapter diff --git a/monitoring/uss_qualifier/resources/definitions.py b/monitoring/uss_qualifier/resources/definitions.py index 50f0800bf9..c744f608a9 100644 --- a/monitoring/uss_qualifier/resources/definitions.py +++ b/monitoring/uss_qualifier/resources/definitions.py @@ -1,4 +1,4 @@ -from typing import Dict, TypeVar +from typing import TypeVar from implicitdict import ImplicitDict @@ -17,7 +17,7 @@ class ResourceDeclaration(ImplicitDict): resource_type: ResourceTypeName """Type of resource, expressed as a Python class name qualified relative to this `resources` module""" - dependencies: Dict[ResourceID, ResourceID] = {} + dependencies: dict[ResourceID, ResourceID] = {} """Mapping of dependency parameter (additional argument to concrete resource constructor) to `name` of resource to use""" specification: dict = {} @@ -25,5 +25,5 @@ class ResourceDeclaration(ImplicitDict): class ResourceCollection(ImplicitDict): - resource_declarations: Dict[ResourceID, ResourceDeclaration] + resource_declarations: dict[ResourceID, ResourceDeclaration] """Mapping of globally (within resource collection) unique name identifying a resource to the declaration of that resource""" diff --git a/monitoring/uss_qualifier/resources/dev/noop.py b/monitoring/uss_qualifier/resources/dev/noop.py index 3fa8caf5e7..c93256ff81 100644 --- a/monitoring/uss_qualifier/resources/dev/noop.py +++ b/monitoring/uss_qualifier/resources/dev/noop.py @@ -12,5 +12,5 @@ class NoOpResource(Resource[NoOpSpecification]): sleep_secs: int def __init__(self, specification: NoOpSpecification, resource_origin: str): - super(NoOpResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.sleep_secs = specification.sleep_secs diff --git a/monitoring/uss_qualifier/resources/dev/test_exclusions.py b/monitoring/uss_qualifier/resources/dev/test_exclusions.py index 083adde405..133f2311ae 100644 --- a/monitoring/uss_qualifier/resources/dev/test_exclusions.py +++ b/monitoring/uss_qualifier/resources/dev/test_exclusions.py @@ -1,13 +1,11 @@ -from typing import Optional - from implicitdict import ImplicitDict from monitoring.uss_qualifier.resources.resource import Resource class TestExclusionsSpecification(ImplicitDict): - allow_private_addresses: Optional[bool] - allow_cleartext_queries: Optional[bool] + allow_private_addresses: bool | None + allow_cleartext_queries: bool | None class TestExclusionsResource(Resource[TestExclusionsSpecification]): @@ -20,7 +18,7 @@ def __init__( specification: TestExclusionsSpecification, resource_origin: str, ): - super(TestExclusionsResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self._spec = specification @property diff --git a/monitoring/uss_qualifier/resources/eurocae/ed269/source_document.py b/monitoring/uss_qualifier/resources/eurocae/ed269/source_document.py index 809257a051..97c2ce312f 100644 --- a/monitoring/uss_qualifier/resources/eurocae/ed269/source_document.py +++ b/monitoring/uss_qualifier/resources/eurocae/ed269/source_document.py @@ -18,6 +18,6 @@ class SourceDocument(Resource[SourceDocumentSpecification]): def __init__( self, specification: SourceDocumentSpecification, resource_origin: str ): - super(SourceDocument, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.specification = specification self.raw_document = fileio.load_content(specification.url) diff --git a/monitoring/uss_qualifier/resources/files.py b/monitoring/uss_qualifier/resources/files.py index eab304eb92..d8ce545e34 100644 --- a/monitoring/uss_qualifier/resources/files.py +++ b/monitoring/uss_qualifier/resources/files.py @@ -1,6 +1,5 @@ import hashlib import json -from typing import Optional from implicitdict import ImplicitDict @@ -12,7 +11,7 @@ class ExternalFile(ImplicitDict): path: FileReference """Location of the external file.""" - hash_sha512: Optional[str] + hash_sha512: str | None """SHA-512 hash of the external file. If specified, the external file's content will be verified to have this hash or else produce an error. diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py b/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py index 02a6f91e46..dc926b47d6 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py @@ -1,7 +1,6 @@ from __future__ import annotations import json -from typing import Dict, List, Optional from implicitdict import ImplicitDict @@ -24,7 +23,7 @@ class DeltaFlightIntent(ImplicitDict): source: FlightIntentID """Base the flight intent for this element of a FlightIntentCollection on the element of the collection identified by this field.""" - mutation: Optional[dict] + mutation: dict | None """For each leaf subfield specified in this object, override the value in the corresponding subfield of the flight intent for this element with the specified value. Consider subfields prefixed with + as leaf subfields.""" @@ -33,27 +32,27 @@ class DeltaFlightIntent(ImplicitDict): class FlightIntentCollectionElement(ImplicitDict): """Definition of a single flight intent within a FlightIntentCollection. Exactly one field must be specified.""" - full: Optional[FlightInfoTemplate] + full: FlightInfoTemplate | None """If specified, the full definition of the flight planning intent.""" - delta: Optional[DeltaFlightIntent] + delta: DeltaFlightIntent | None """If specified, a flight planning intent based on another flight intent, but with some changes.""" class FlightIntentCollection(ImplicitDict): """Specification for a collection of flight intents, each identified by a FlightIntentID.""" - intents: Dict[FlightIntentID, FlightIntentCollectionElement] + intents: dict[FlightIntentID, FlightIntentCollectionElement] """Flight planning actions that users want to perform.""" - transformations: Optional[List[Transformation]] + transformations: list[Transformation] | None """Transformations to append to all FlightInfoTemplates.""" - def resolve(self) -> Dict[FlightIntentID, FlightInfoTemplate]: + def resolve(self) -> dict[FlightIntentID, FlightInfoTemplate]: """Resolve the underlying delta flight intents.""" # process intents in order of dependency to resolve deltas - processed_intents: Dict[FlightIntentID, FlightInfoTemplate] = {} + processed_intents: dict[FlightIntentID, FlightInfoTemplate] = {} unprocessed_intent_ids = list(self.intents.keys()) while unprocessed_intent_ids: @@ -108,11 +107,11 @@ def resolve(self) -> Dict[FlightIntentID, FlightInfoTemplate]: class FlightIntentsSpecification(ImplicitDict): """Exactly one field must be specified.""" - intent_collection: Optional[FlightIntentCollection] + intent_collection: FlightIntentCollection | None """Full flight intent collection, or a $ref to an external file containing a FlightIntentCollection.""" - file: Optional[ExternalFile] + file: ExternalFile | None """Location of file to load, containing a FlightIntentCollection""" - transformations: Optional[List[Transformation]] + transformations: list[Transformation] | None """Transformations to apply to all flight intents' 4D volumes after resolution (if specified)""" diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_intent_validation.py b/monitoring/uss_qualifier/resources/flight_planning/flight_intent_validation.py index bf8f9a0ee7..c5d1e4991c 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_intent_validation.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_intent_validation.py @@ -1,6 +1,6 @@ +from collections.abc import Iterator from dataclasses import dataclass from datetime import timedelta -from typing import Dict, Iterator, List, Optional import arrow from implicitdict import StringBasedTimeDelta @@ -27,25 +27,25 @@ @dataclass -class ExpectedFlightIntent(object): +class ExpectedFlightIntent: intent_id: FlightIntentID name: FlightIntentName - must_conflict_with: Optional[List[FlightIntentName]] = None - must_not_conflict_with: Optional[List[FlightIntentName]] = None - usage_state: Optional[AirspaceUsageState] = None - uas_state: Optional[UasState] = None - f3548v21_priority_higher_than: Optional[List[FlightIntentName]] = None - f3548v21_priority_equal_to: Optional[List[FlightIntentName]] = None - earliest_time_start: Optional[StringBasedTimeDelta] = None - latest_time_start: Optional[StringBasedTimeDelta] = None - earliest_time_end: Optional[StringBasedTimeDelta] = None - latest_time_end: Optional[StringBasedTimeDelta] = None - valid_uspace_flight_auth: Optional[bool] = None + must_conflict_with: list[FlightIntentName] | None = None + must_not_conflict_with: list[FlightIntentName] | None = None + usage_state: AirspaceUsageState | None = None + uas_state: UasState | None = None + f3548v21_priority_higher_than: list[FlightIntentName] | None = None + f3548v21_priority_equal_to: list[FlightIntentName] | None = None + earliest_time_start: StringBasedTimeDelta | None = None + latest_time_start: StringBasedTimeDelta | None = None + earliest_time_end: StringBasedTimeDelta | None = None + latest_time_end: StringBasedTimeDelta | None = None + valid_uspace_flight_auth: bool | None = None def validate_flight_intent_templates( - templates: Dict[FlightIntentID, FlightInfoTemplate], - expected_intents: List[ExpectedFlightIntent], + templates: dict[FlightIntentID, FlightInfoTemplate], + expected_intents: list[ExpectedFlightIntent], ) -> Volume4D: """ Returns: the bounding extents of the flight intent templates @@ -78,8 +78,8 @@ def validate_flight_intent_templates( def validate_flight_intents( - intents: Dict[FlightIntentID, FlightInfo], - expected_intents: List[ExpectedFlightIntent], + intents: dict[FlightIntentID, FlightInfo], + expected_intents: list[ExpectedFlightIntent], now: Time, ) -> None: """Validate that `intents` contains all intents meeting all the criteria in `expected_intents`. diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py b/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py index 5bd421aad5..e4189a1109 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py @@ -1,5 +1,3 @@ -from typing import Dict - from implicitdict import ImplicitDict from monitoring.monitorlib.clients.flight_planning.flight_info_template import ( @@ -18,7 +16,7 @@ class FlightIntentsResource(Resource[FlightIntentsSpecification]): _intent_collection: FlightIntentCollection def __init__(self, specification: FlightIntentsSpecification, resource_origin: str): - super(FlightIntentsResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) has_file = "file" in specification and specification.file has_literal = ( "intent_collection" in specification and specification.intent_collection @@ -48,5 +46,5 @@ def __init__(self, specification: FlightIntentsSpecification, resource_origin: s else: self._intent_collection.transformations = specification.transformations - def get_flight_intents(self) -> Dict[FlightIntentID, FlightInfoTemplate]: + def get_flight_intents(self) -> dict[FlightIntentID, FlightInfoTemplate]: return self._intent_collection.resolve() diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py b/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py index 3e5fac4f19..896933465e 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py @@ -1,4 +1,3 @@ -from typing import Optional from urllib.parse import urlparse from implicitdict import ImplicitDict @@ -17,13 +16,13 @@ class FlightPlannerConfiguration(ImplicitDict): participant_id: str """ID of the flight planner into which test data can be injected""" - scd_injection_base_url: Optional[str] + scd_injection_base_url: str | None """Base URL for the flight planner's implementation of the interfaces/automated_testing/scd/v1/scd.yaml API""" - v1_base_url: Optional[str] + v1_base_url: str | None """Base URL for the flight planner's implementation of the interfaces/automated_testing/flight_planning/v1/flight_planning.yaml API""" - timeout_seconds: Optional[float] = None + timeout_seconds: float | None = None """Number of seconds to allow for requests to this flight planner. If None, use default.""" def __init__(self, *args, **kwargs): diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_planners.py b/monitoring/uss_qualifier/resources/flight_planning/flight_planners.py index 5bfe43b89c..847c0688f5 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_planners.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_planners.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterable, List, Optional +from collections.abc import Iterable from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.flight_planning.v1.constants import ( @@ -30,7 +30,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(FlightPlannerResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) if ( "scd_injection_base_url" in specification.flight_planner and specification.flight_planner.scd_injection_base_url @@ -62,11 +62,11 @@ def __init__( class FlightPlannersSpecification(ImplicitDict): - flight_planners: List[FlightPlannerConfiguration] + flight_planners: list[FlightPlannerConfiguration] class FlightPlannersResource(Resource[FlightPlannersSpecification]): - flight_planners: List[FlightPlannerResource] + flight_planners: list[FlightPlannerResource] def __init__( self, @@ -74,7 +74,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(FlightPlannersResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self._specification = specification self._auth_adapter = auth_adapter self.flight_planners = [ @@ -86,15 +86,15 @@ def __init__( for i, p in enumerate(specification.flight_planners) ] - def make_subset(self, select_indices: Iterable[int]) -> List[FlightPlannerResource]: + def make_subset(self, select_indices: Iterable[int]) -> list[FlightPlannerResource]: return [self.flight_planners[i] for i in select_indices] class FlightPlannerCombinationSelectorSpecification(ImplicitDict): - must_include: Optional[List[ParticipantID]] + must_include: list[ParticipantID] | None """The set of flight planners which must be included in every combination""" - maximum_roles: Optional[Dict[ParticipantID, int]] + maximum_roles: dict[ParticipantID, int] | None """Maximum number of roles a particular participant may fill in any given combination""" @@ -108,13 +108,11 @@ def __init__( specification: FlightPlannerCombinationSelectorSpecification, resource_origin: str, ): - super(FlightPlannerCombinationSelectorResource, self).__init__( - specification, resource_origin - ) + super().__init__(specification, resource_origin) self._specification = specification def is_valid_combination( - self, flight_planners: Dict[ResourceID, FlightPlannerResource] + self, flight_planners: dict[ResourceID, FlightPlannerResource] ): participants = [p.participant_id for p in flight_planners.values()] diff --git a/monitoring/uss_qualifier/resources/geospatial_info/geospatial_info_providers.py b/monitoring/uss_qualifier/resources/geospatial_info/geospatial_info_providers.py index 46d2b518b4..343d9d244f 100644 --- a/monitoring/uss_qualifier/resources/geospatial_info/geospatial_info_providers.py +++ b/monitoring/uss_qualifier/resources/geospatial_info/geospatial_info_providers.py @@ -1,4 +1,3 @@ -from typing import Optional from urllib.parse import urlparse from implicitdict import ImplicitDict @@ -20,10 +19,10 @@ class GeospatialInfoProviderConfiguration(ImplicitDict): participant_id: str """ID of the geospatial information provider for which geospatial data can be queried""" - geospatial_map_v1_base_url: Optional[str] + geospatial_map_v1_base_url: str | None """Base URL for the geospatial information provider's implementation of the interfaces/automated_testing/geospatial_map/v1/geospatial_map.yaml API""" - timeout_seconds: Optional[float] = None + timeout_seconds: float | None = None """Number of seconds to allow for requests to this geospatial information provider. If None, use default.""" def __init__(self, *args, **kwargs): @@ -61,9 +60,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(GeospatialInfoProviderResource, self).__init__( - specification, resource_origin - ) + super().__init__(specification, resource_origin) if ( "geospatial_map_v1_base_url" in specification.geospatial_info_provider and specification.geospatial_info_provider.geospatial_map_v1_base_url diff --git a/monitoring/uss_qualifier/resources/interuss/datastore/datastore.py b/monitoring/uss_qualifier/resources/interuss/datastore/datastore.py index b5d30b3086..6febf008c9 100644 --- a/monitoring/uss_qualifier/resources/interuss/datastore/datastore.py +++ b/monitoring/uss_qualifier/resources/interuss/datastore/datastore.py @@ -1,7 +1,6 @@ from __future__ import annotations import socket -from typing import List, Optional, Tuple import psycopg from implicitdict import ImplicitDict @@ -31,7 +30,7 @@ def __init__( specification: DatastoreDBNodeSpecification, resource_origin: str, ): - super(DatastoreDBNodeResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self._specification = specification def get_client(self) -> DatastoreDBNode: @@ -50,18 +49,18 @@ def participant_id(self) -> str: class DatastoreDBClusterSpecification(ImplicitDict): - nodes: List[DatastoreDBNodeSpecification] + nodes: list[DatastoreDBNodeSpecification] class DatastoreDBClusterResource(Resource[DatastoreDBClusterSpecification]): - nodes: List[DatastoreDBNodeResource] + nodes: list[DatastoreDBNodeResource] def __init__( self, specification: DatastoreDBClusterSpecification, resource_origin: str, ): - super(DatastoreDBClusterResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.nodes = [ DatastoreDBNodeResource( specification=s, resource_origin=f"node {i + 1} in {resource_origin}" @@ -70,7 +69,7 @@ def __init__( ] -class DatastoreDBNode(object): +class DatastoreDBNode: participant_id: str host: str port: int @@ -93,7 +92,7 @@ def connect(self, **kwargs) -> psycopg.Connection: **kwargs, ) - def is_reachable(self) -> Tuple[bool, Optional[psycopg.Error]]: + def is_reachable(self) -> tuple[bool, psycopg.Error | None]: """ Returns True if the node is reachable. This is detected by attempting to establish a connection with the node @@ -118,7 +117,7 @@ def is_reachable(self) -> Tuple[bool, Optional[psycopg.Error]]: return is_reachable, e return True, None - def runs_in_secure_mode(self) -> Tuple[bool, Optional[psycopg.Error]]: + def runs_in_secure_mode(self) -> tuple[bool, psycopg.Error | None]: """ Returns True if the node is running in secure mode. This is detected by attempting to establish a connection with the node @@ -139,7 +138,7 @@ def runs_in_secure_mode(self) -> Tuple[bool, Optional[psycopg.Error]]: return secure_mode, e return False, None - def legacy_ssl_version_rejected(self) -> Tuple[bool, Optional[psycopg.Error]]: + def legacy_ssl_version_rejected(self) -> tuple[bool, psycopg.Error | None]: """ Returns True if the node rejects the usage of the legacy cryptographic protocols TLSv1 and TLSv1.1. diff --git a/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py b/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py index 15fa489d60..b45d017cf2 100644 --- a/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py +++ b/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import List from implicitdict import ImplicitDict @@ -35,7 +34,7 @@ class FlightCheck(ImplicitDict): flight_check_id: str """Unique (within table) test step/row identifier.""" - requirement_ids: List[str] + requirement_ids: list[str] """Jurisdictional identifiers of the requirements this test step is evaluating.""" description: str @@ -55,4 +54,4 @@ class FlightCheck(ImplicitDict): class FlightCheckTable(ImplicitDict): - rows: List[FlightCheck] + rows: list[FlightCheck] diff --git a/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py b/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py index 4f37cd8442..644d8c48f4 100644 --- a/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py +++ b/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py @@ -16,5 +16,5 @@ class FlightCheckTableResource(Resource[FlightCheckTableSpecification]): def __init__( self, specification: FlightCheckTableSpecification, resource_origin: str ): - super(FlightCheckTableResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.table = specification.table diff --git a/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py b/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py index 2c33dd5c34..5ecbd834a0 100644 --- a/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py +++ b/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import List, Optional from implicitdict import ImplicitDict @@ -21,13 +20,13 @@ class FeatureCheck(ImplicitDict): geospatial_check_id: str """Unique (within table) test step/row identifier.""" - requirement_ids: List[str] + requirement_ids: list[str] """Jurisdictional identifiers of the requirements this test step is evaluating.""" description: str """Human-readable test step description to aid in the debugging and traceability.""" - operation_rule_set: Optional[str] = None + operation_rule_set: str | None = None """The set of operating rules (or rule set) under which the operation described in the feature check should be performed.""" volumes: Volume4DTemplateCollection @@ -36,7 +35,7 @@ class FeatureCheck(ImplicitDict): A service provider is expected to provide geospatial features relevant to any of the entire area specified and for any of the entire time specified. """ - restriction_source: Optional[str] = None + restriction_source: str | None = None """Which source for geospatial features describing restrictions should be considered when looking for the expected outcome.""" expected_result: ExpectedFeatureCheckResult @@ -44,4 +43,4 @@ class FeatureCheck(ImplicitDict): class FeatureCheckTable(ImplicitDict): - rows: List[FeatureCheck] + rows: list[FeatureCheck] diff --git a/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py b/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py index b94748a0d1..3acdad1da1 100644 --- a/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py +++ b/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py @@ -16,5 +16,5 @@ class FeatureCheckTableResource(Resource[FeatureCheckTableSpecification]): def __init__( self, specification: FeatureCheckTableSpecification, resource_origin: str ): - super(FeatureCheckTableResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.table = specification.table diff --git a/monitoring/uss_qualifier/resources/interuss/id_generator.py b/monitoring/uss_qualifier/resources/interuss/id_generator.py index bfa61bb1fe..00e8d0859e 100644 --- a/monitoring/uss_qualifier/resources/interuss/id_generator.py +++ b/monitoring/uss_qualifier/resources/interuss/id_generator.py @@ -21,7 +21,7 @@ def __init__( resource_origin: str, client_identity: ClientIdentityResource, ): - super(IDGeneratorResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self._client_identity = client_identity @property diff --git a/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py b/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py index 2c1bd7453c..39d9e88bec 100644 --- a/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py +++ b/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py @@ -1,5 +1,3 @@ -from typing import List, Optional, Tuple - from implicitdict import ImplicitDict, StringBasedDateTime from monitoring.monitorlib import fetch @@ -28,7 +26,7 @@ MOCK_USS_CONFIG_SCOPE = "interuss.mock_uss.configure" -class MockUSSClient(object): +class MockUSSClient: """Means to communicate with an InterUSS mock_uss instance""" flight_planner: FlightPlannerClient @@ -38,7 +36,7 @@ def __init__( participant_id: str, base_url: str, auth_adapter: AuthAdapter, - timeout_seconds: Optional[float] = None, + timeout_seconds: float | None = None, ): self.base_url = base_url self.session = UTMClientSession(base_url, auth_adapter, timeout_seconds) @@ -57,7 +55,7 @@ def get_status(self) -> fetch.Query: participant_id=self.participant_id, ) - def get_locality(self) -> Tuple[Optional[LocalityCode], fetch.Query]: + def get_locality(self) -> tuple[LocalityCode | None, fetch.Query]: query = fetch.query_and_describe( self.session, "GET", @@ -88,7 +86,7 @@ def set_locality(self, locality_code: LocalityCode) -> fetch.Query: def get_interactions( self, from_time: StringBasedDateTime - ) -> Tuple[List[Interaction], fetch.Query]: + ) -> tuple[list[Interaction], fetch.Query]: """ Requesting interuss interactions from mock_uss from a given time till now Args: @@ -96,9 +94,7 @@ def get_interactions( Returns: List of Interactions """ - url = "{}/mock_uss/interuss_logging/logs?from_time={}".format( - self.base_url, from_time - ) + url = f"{self.base_url}/mock_uss/interuss_logging/logs?from_time={from_time}" query = fetch.query_and_describe( self.session, "GET", @@ -140,7 +136,7 @@ class MockUSSSpecification(ImplicitDict): participant_id: ParticipantID """Test participant responsible for this mock USS.""" - timeout_seconds: Optional[float] = None + timeout_seconds: float | None = None """Number of seconds to allow for requests to this mock_uss instance. If None, use default.""" @@ -153,7 +149,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(MockUSSResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.mock_uss = MockUSSClient( specification.participant_id, specification.mock_uss_base_url, @@ -163,11 +159,11 @@ def __init__( class MockUSSsSpecification(ImplicitDict): - instances: List[MockUSSSpecification] + instances: list[MockUSSSpecification] class MockUSSsResource(Resource[MockUSSsSpecification]): - mock_uss_instances: List[MockUSSClient] + mock_uss_instances: list[MockUSSClient] def __init__( self, @@ -175,7 +171,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(MockUSSsResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.mock_uss_instances = [ MockUSSClient(s.participant_id, s.mock_uss_base_url, auth_adapter.adapter) for s in specification.instances diff --git a/monitoring/uss_qualifier/resources/interuss/mock_uss/locality.py b/monitoring/uss_qualifier/resources/interuss/mock_uss/locality.py index edabf762a9..7e15ee6d07 100644 --- a/monitoring/uss_qualifier/resources/interuss/mock_uss/locality.py +++ b/monitoring/uss_qualifier/resources/interuss/mock_uss/locality.py @@ -12,7 +12,7 @@ class LocalityResource(Resource[LocalitySpecification]): locality_code: LocalityCode def __init__(self, specification: LocalitySpecification, resource_origin: str): - super(LocalityResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) # Make sure provided code is valid Locality.from_locale(specification.locality_code) diff --git a/monitoring/uss_qualifier/resources/interuss/query_behavior.py b/monitoring/uss_qualifier/resources/interuss/query_behavior.py index 1ed1271f3f..0259a5004a 100644 --- a/monitoring/uss_qualifier/resources/interuss/query_behavior.py +++ b/monitoring/uss_qualifier/resources/interuss/query_behavior.py @@ -1,5 +1,3 @@ -from typing import Optional - from implicitdict import ImplicitDict from loguru import logger @@ -8,19 +6,19 @@ class QueryBehaviorSpecification(ImplicitDict): - connect_timeout_seconds: Optional[float] + connect_timeout_seconds: float | None """Number of seconds to allow for establishing a connection. Use 0 for no timeout.""" - read_timeout_seconds: Optional[float] + read_timeout_seconds: float | None """Number of seconds to allow for a request to complete after establishing a connection. Use 0 for no timeout.""" - attempts: Optional[int] + attempts: int | None """Number of attempts to query when experiencing a retryable error like a timeout""" - add_request_id: Optional[bool] + add_request_id: bool | None """Whether to automatically add a `request_id` field to any request with a JSON body and no pre-existing `request_id` field""" - fake_netlocs: Optional[list[str]] + fake_netlocs: list[str] | None """Network locations well-known to be fake and for which a request should fail immediately without being attempted.""" @@ -36,7 +34,7 @@ def __init__( specification: QueryBehaviorSpecification, resource_origin: str, ): - super(QueryBehaviorResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) if ( "connect_timeout_seconds" in specification diff --git a/monitoring/uss_qualifier/resources/interuss/uss_identification.py b/monitoring/uss_qualifier/resources/interuss/uss_identification.py index 34eb124a14..6fb0b82ed6 100644 --- a/monitoring/uss_qualifier/resources/interuss/uss_identification.py +++ b/monitoring/uss_qualifier/resources/interuss/uss_identification.py @@ -1,5 +1,4 @@ import re -from typing import Dict, List, Optional from implicitdict import ImplicitDict @@ -9,28 +8,28 @@ class AccessTokenIdentifier(ImplicitDict): - issuer: Optional[str] + issuer: str | None """If specified, this identifier only applies to access tokens from this issuer. If not specified, this identifier applies to any access token.""" - subject: Optional[str] + subject: str | None """If specified, assume the participant is responsible for applicable access tokens containing this subject.""" class USSIdentifiers(ImplicitDict): - astm_url_regexes: Optional[List[str]] + astm_url_regexes: list[str] | None """If a URL to an ASTM (F3411, F3548, etc) endpoint matches one of these regular expressions, assume the participant is responsible for that server""" - access_tokens: Optional[List[AccessTokenIdentifier]] + access_tokens: list[AccessTokenIdentifier] | None """If an access token matches one of these identifiers, assume the participant is responsible for that access token""" class USSIdentificationSpecification(ImplicitDict): - uss_identifiers: Dict[ParticipantID, USSIdentifiers] + uss_identifiers: dict[ParticipantID, USSIdentifiers] """For each specified participant, a set of information that allows actions, resources, etc to be associated with that participant.""" class USSIdentificationResource(Resource[USSIdentificationSpecification]): - identifiers: Dict[ParticipantID, USSIdentifiers] + identifiers: dict[ParticipantID, USSIdentifiers] """"For each specified participant, a set of information that allows actions, resources, etc to be associated with that participant. Guaranteed not None.""" def __init__( @@ -38,7 +37,7 @@ def __init__( specification: USSIdentificationSpecification, resource_origin: str, ): - super(USSIdentificationResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.identifiers = specification.uss_identifiers or {} def attribute_query(self, query: Query) -> None: diff --git a/monitoring/uss_qualifier/resources/netrid/evaluation.py b/monitoring/uss_qualifier/resources/netrid/evaluation.py index 576dda5049..cd5bdef693 100644 --- a/monitoring/uss_qualifier/resources/netrid/evaluation.py +++ b/monitoring/uss_qualifier/resources/netrid/evaluation.py @@ -21,7 +21,5 @@ class EvaluationConfigurationResource(Resource[EvaluationConfiguration]): configuration: EvaluationConfiguration def __init__(self, specification: EvaluationConfiguration, resource_origin: str): - super(EvaluationConfigurationResource, self).__init__( - specification, resource_origin - ) + super().__init__(specification, resource_origin) self.configuration = specification diff --git a/monitoring/uss_qualifier/resources/netrid/flight_data.py b/monitoring/uss_qualifier/resources/netrid/flight_data.py index 5e295e7049..0408374412 100644 --- a/monitoring/uss_qualifier/resources/netrid/flight_data.py +++ b/monitoring/uss_qualifier/resources/netrid/flight_data.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from implicitdict import ImplicitDict, StringBasedDateTime, StringBasedTimeDelta from uas_standards.interuss.automated_testing.rid.v1 import injection @@ -10,7 +8,7 @@ class FullFlightRecord(ImplicitDict): reference_time: StringBasedDateTime """The reference time of this flight (usually the time of first telemetry)""" - states: List[injection.RIDAircraftState] + states: list[injection.RIDAircraftState] """All telemetry that will be/was received for this flight""" flight_details: injection.RIDFlightDetails @@ -21,7 +19,7 @@ class FullFlightRecord(ImplicitDict): class FlightRecordCollection(ImplicitDict): - flights: List[FullFlightRecord] + flights: list[FullFlightRecord] class AdjacentCircularFlightsSimulatorConfiguration(ImplicitDict): @@ -32,7 +30,7 @@ class AdjacentCircularFlightsSimulatorConfiguration(ImplicitDict): relative to a time close to the time of test. """ - random_seed: Optional[int] = 12345 + random_seed: int | None = 12345 """Pseudorandom seed that should be used, or specify None to use default Random.""" minx: float = 7.4735784530639648 @@ -65,7 +63,7 @@ class FlightDataKMLFileConfiguration(ImplicitDict): relative to a time close to the time of test. """ - random_seed: Optional[int] = 12345 + random_seed: int | None = 12345 """Pseudorandom seed that should be used, or specify None to use default Random.""" kml_file: ExternalFile @@ -76,13 +74,13 @@ class FlightDataSpecification(ImplicitDict): flight_start_delay: StringBasedTimeDelta = StringBasedTimeDelta("15s") """Amount of time between starting the test and commencement of flights""" - record_source: Optional[ExternalFile] + record_source: ExternalFile | None """When this field is populated, flight record data will be loaded directly from this file""" - kml_source: Optional[FlightDataKMLFileConfiguration] + kml_source: FlightDataKMLFileConfiguration | None """When this field is populated, flight data will be generated from a KML file""" - adjacent_circular_flights_simulation_source: Optional[ - AdjacentCircularFlightsSimulatorConfiguration - ] + adjacent_circular_flights_simulation_source: ( + AdjacentCircularFlightsSimulatorConfiguration | None + ) """When this field is populated, flight data will be simulated with the AdjacentCircularFlightsSimulator""" diff --git a/monitoring/uss_qualifier/resources/netrid/flight_data_resources.py b/monitoring/uss_qualifier/resources/netrid/flight_data_resources.py index d698d22d26..5616325428 100644 --- a/monitoring/uss_qualifier/resources/netrid/flight_data_resources.py +++ b/monitoring/uss_qualifier/resources/netrid/flight_data_resources.py @@ -2,7 +2,7 @@ import json import uuid from datetime import timedelta -from typing import List, Optional, Self +from typing import Self import arrow from implicitdict import ImplicitDict, StringBasedDateTime @@ -34,7 +34,7 @@ class FlightDataResource(Resource[FlightDataSpecification]): _field_to_clean = None def __init__(self, specification: FlightDataSpecification, resource_origin: str): - super(FlightDataResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) if "record_source" in specification: self.flight_collection = ImplicitDict.parse( load_dict(specification.record_source), @@ -58,15 +58,15 @@ def __init__(self, specification: FlightDataSpecification, resource_origin: str) ) self._flight_start_delay = specification.flight_start_delay.timedelta - def get_test_flights(self) -> List[TestFlight]: + def get_test_flights(self) -> list[TestFlight]: t0 = arrow.utcnow() + self._flight_start_delay - test_flights: List[TestFlight] = [] + test_flights: list[TestFlight] = [] for flight in self.flight_collection.flights: dt = t0 - flight.reference_time.datetime - telemetry: List[RIDAircraftState] = [] + telemetry: list[RIDAircraftState] = [] removed_field = False @@ -181,10 +181,10 @@ def drop_every_n_state(self, n: int) -> Self: class FlightDataStorageSpecification(ImplicitDict): - flight_record_collection_path: Optional[str] + flight_record_collection_path: str | None """Path, usually ending with .json, at which to store the FlightRecordCollection""" - geojson_tracks_path: Optional[str] + geojson_tracks_path: str | None """Path (folder) in which to store track_XX.geojson files, 1 for each flight""" @@ -194,5 +194,5 @@ class FlightDataStorageResource(Resource[FlightDataStorageSpecification]): def __init__( self, specification: FlightDataStorageSpecification, resource_origin: str ): - super(FlightDataStorageResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.storage_configuration = specification diff --git a/monitoring/uss_qualifier/resources/netrid/observers.py b/monitoring/uss_qualifier/resources/netrid/observers.py index 5dfdb4d1af..9d8dd3e550 100644 --- a/monitoring/uss_qualifier/resources/netrid/observers.py +++ b/monitoring/uss_qualifier/resources/netrid/observers.py @@ -1,5 +1,3 @@ -from typing import List, Optional, Tuple - import s2sphere from implicitdict import ImplicitDict from loguru import logger @@ -15,7 +13,7 @@ from monitoring.uss_qualifier.resources.resource import Resource -class RIDSystemObserver(object): +class RIDSystemObserver: participant_id: str base_url: str session: infrastructure.UTMClientSession @@ -32,13 +30,8 @@ def __init__( def observe_system( self, rect: s2sphere.LatLngRect - ) -> Tuple[Optional[observation_api.GetDisplayDataResponse], fetch.Query]: - url = "/display_data?view={},{},{},{}".format( - rect.lo().lat().degrees, - rect.lo().lng().degrees, - rect.hi().lat().degrees, - rect.hi().lng().degrees, - ) + ) -> tuple[observation_api.GetDisplayDataResponse | None, fetch.Query]: + url = f"/display_data?view={rect.lo().lat().degrees},{rect.lo().lng().degrees},{rect.hi().lat().degrees},{rect.hi().lng().degrees}" query = fetch.query_and_describe( self.session, "GET", @@ -62,7 +55,7 @@ def observe_system( def observe_flight_details( self, flight_id: str - ) -> Tuple[Optional[observation_api.GetDetailsResponse], fetch.Query]: + ) -> tuple[observation_api.GetDetailsResponse | None, fetch.Query]: query = fetch.query_and_describe( self.session, "GET", @@ -96,11 +89,11 @@ class ObserverConfiguration(ImplicitDict): class NetRIDObserversSpecification(ImplicitDict): - observers: List[ObserverConfiguration] + observers: list[ObserverConfiguration] class NetRIDObserversResource(Resource[NetRIDObserversSpecification]): - observers: List[RIDSystemObserver] + observers: list[RIDSystemObserver] def __init__( self, @@ -108,7 +101,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(NetRIDObserversResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) auth_adapter.assert_scopes_available( scopes_required={ Scope.Observe: "observe RID flights visible to user from USSs under test" diff --git a/monitoring/uss_qualifier/resources/netrid/service_area.py b/monitoring/uss_qualifier/resources/netrid/service_area.py index b5ccdec0eb..c00b5279a9 100644 --- a/monitoring/uss_qualifier/resources/netrid/service_area.py +++ b/monitoring/uss_qualifier/resources/netrid/service_area.py @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List +from typing import Any from implicitdict import ImplicitDict, StringBasedDateTime @@ -15,7 +15,7 @@ class ServiceAreaSpecification(ImplicitDict): This URL will probably not identify a real resource in tests.""" - footprint: List[LatLngPoint] + footprint: list[LatLngPoint] """2D outline of service area""" altitude_min: float = 0 @@ -47,7 +47,7 @@ def shifted_time_end( def get_new_subscription_params( self, sub_id: str, start_time: datetime.datetime, duration: datetime.timedelta - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Builds a dict of parameters that can be used to create a subscription, using this ISA's parameters and the passed start time and duration @@ -67,5 +67,5 @@ class ServiceAreaResource(Resource[ServiceAreaSpecification]): specification: ServiceAreaSpecification def __init__(self, specification: ServiceAreaSpecification, resource_origin: str): - super(ServiceAreaResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.specification = specification diff --git a/monitoring/uss_qualifier/resources/netrid/service_providers.py b/monitoring/uss_qualifier/resources/netrid/service_providers.py index 0f6c3e335e..8654a4eea1 100644 --- a/monitoring/uss_qualifier/resources/netrid/service_providers.py +++ b/monitoring/uss_qualifier/resources/netrid/service_providers.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional, Tuple from urllib.parse import urlparse from implicitdict import ImplicitDict @@ -35,10 +34,10 @@ def __init__(self, *args, **kwargs): class NetRIDServiceProvidersSpecification(ImplicitDict): - service_providers: List[ServiceProviderConfiguration] + service_providers: list[ServiceProviderConfiguration] -class NetRIDServiceProvider(object): +class NetRIDServiceProvider: participant_id: str injection_base_url: str injection_client: infrastructure.UTMClientSession @@ -80,7 +79,7 @@ def get_user_notifications( self, after: datetime.datetime, before: datetime.datetime, - ) -> Tuple[Optional[QueryUserNotificationsResponse], fetch.Query]: + ) -> tuple[QueryUserNotificationsResponse | None, fetch.Query]: q = fetch.query_and_describe( self.injection_client, "GET", @@ -108,7 +107,7 @@ def get_user_notifications( class NetRIDServiceProviders(Resource[NetRIDServiceProvidersSpecification]): - service_providers: List[NetRIDServiceProvider] + service_providers: list[NetRIDServiceProvider] def __init__( self, @@ -116,7 +115,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(NetRIDServiceProviders, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) auth_adapter.assert_scopes_available( scopes_required={ SCOPE_RID_QUALIFIER_INJECT: "inject RID test flight data into USSs under test" diff --git a/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py b/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py index 2590ee110a..76c7b72a86 100644 --- a/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py +++ b/monitoring/uss_qualifier/resources/netrid/simulation/adjacent_circular_flights_simulator.py @@ -1,6 +1,5 @@ import random from datetime import timedelta -from typing import List import arrow import shapely.geometry @@ -44,13 +43,13 @@ def __init__(self, config: AdjacentCircularFlightsSimulatorConfiguration) -> Non self.altitude_agl = 50.0 - self.grid_cells_flight_tracks: List[GridCellFlight] = [] + self.grid_cells_flight_tracks: list[GridCellFlight] = [] # This object holds the name and the polygon object of the query boxes. The number of bboxes are controlled by the `box_diagonals` variable - self.query_bboxes: List[QueryBoundingBox] = [] + self.query_bboxes: list[QueryBoundingBox] = [] - self.flights: List[FullFlightRecord] = [] - self.bbox_center: List[shapely.geometry.Point] = [] + self.flights: list[FullFlightRecord] = [] + self.bbox_center: list[shapely.geometry.Point] = [] self.geod = Geod(ellps="WGS84") @@ -136,8 +135,8 @@ def generate_query_bboxes(self): ) def generate_flight_speed_bearing( - self, adjacent_points: List, delta_time_secs: int - ) -> List[float]: + self, adjacent_points: list, delta_time_secs: int + ) -> list[float]: """A method to generate flight speed, assume that the flight has to traverse two adjecent points in x number of seconds provided, calculating speed in meters / second. It also generates bearing between this and next point, this is used to populate the 'track' paramater in the Aircraft State JSON.""" first_point = adjacent_points[0] @@ -148,7 +147,7 @@ def generate_flight_speed_bearing( ) speed_mts_per_sec = adjacent_point_distance_mts / delta_time_secs - speed_mts_per_sec = float("{:.2f}".format(speed_mts_per_sec)) + speed_mts_per_sec = float(f"{speed_mts_per_sec:.2f}") if fwd_azimuth < 0: fwd_azimuth = 360 + fwd_azimuth @@ -173,9 +172,7 @@ def utm_converter( elif point_or_polygon == "Point": new_coordinates = proj(*coordinates, inverse=inverse) else: - raise RuntimeError( - "Unexpected geo_interface type: {}".format(point_or_polygon) - ) + raise RuntimeError(f"Unexpected geo_interface type: {point_or_polygon}") return shapely.geometry.shape( {"type": point_or_polygon, "coordinates": tuple(new_coordinates)} @@ -267,7 +264,7 @@ def generate_rid_state(self, duration): """ - all_flight_telemetry: List[List[injection.RIDAircraftState]] = [] + all_flight_telemetry: list[list[injection.RIDAircraftState]] = [] flight_track_details = {} # Develop a index of flight length and their index # Store where on the track the current index is, since the tracks are circular, once the end of the track is reached, the index is reset to 0 to indicate beginning of the track again. flight_current_index = {} diff --git a/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py b/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py index de9ede1063..6784ff9a2a 100755 --- a/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py +++ b/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py @@ -6,7 +6,6 @@ import uuid from collections import namedtuple from datetime import timedelta -from typing import List import s2sphere from implicitdict import StringBasedDateTime @@ -196,7 +195,7 @@ def generate_flight_record( # Reference used to compute vertical speed last_alt = state_coordinates[0].alt if state_coordinates else 0 - flight_telemetry: List[injection.RIDAircraftState] = [] + flight_telemetry: list[injection.RIDAircraftState] = [] for coordinates, speed, angle in zip( state_coordinates, flight_state_speeds, flight_track_angles ): diff --git a/monitoring/uss_qualifier/resources/netrid/simulation/utils.py b/monitoring/uss_qualifier/resources/netrid/simulation/utils.py index ef4899d336..0c1db3e609 100644 --- a/monitoring/uss_qualifier/resources/netrid/simulation/utils.py +++ b/monitoring/uss_qualifier/resources/netrid/simulation/utils.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import List, NamedTuple +from typing import NamedTuple import shapely.geometry from shapely.geometry import Polygon @@ -28,4 +28,4 @@ class GridCellFlight(NamedTuple): """A object to hold details of a grid location and the track within it""" bounds: shapely.geometry.polygon.Polygon - track: List[FlightPoint] + track: list[FlightPoint] diff --git a/monitoring/uss_qualifier/resources/overrides.py b/monitoring/uss_qualifier/resources/overrides.py index 65b6e49f9f..88f8eca6e4 100644 --- a/monitoring/uss_qualifier/resources/overrides.py +++ b/monitoring/uss_qualifier/resources/overrides.py @@ -7,7 +7,7 @@ ImplicitDictType = TypeVar("ImplicitDictType", bound=ImplicitDict) -def apply_overrides( +def apply_overrides[ImplicitDictType: ImplicitDict]( base_object: ImplicitDictType, overrides: dict, parse_result: bool = True ) -> ImplicitDictType: """Returns a copy of base_object onto which overrides were applied.""" diff --git a/monitoring/uss_qualifier/resources/planning_area.py b/monitoring/uss_qualifier/resources/planning_area.py index 2b5075d8e4..abb48a29aa 100644 --- a/monitoring/uss_qualifier/resources/planning_area.py +++ b/monitoring/uss_qualifier/resources/planning_area.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21.api import ( @@ -23,7 +22,7 @@ class PlanningAreaSpecification(ImplicitDict): """Specifies a 2D or 3D volume along with USS related information to create test resources that require them.""" - base_url: Optional[str] + base_url: str | None """Base URL for the USS Note that this is the base URL for the F3548-21 USS API, not the flights or any other specific URL. @@ -84,14 +83,14 @@ def get_new_subscription_params( def get_new_operational_intent_ref_params( self, - key: List[EntityOVN], + key: list[EntityOVN], state: OperationalIntentState, uss_base_url: UssBaseURL, time_start: datetime.datetime, time_end: datetime.datetime, - subscription_id: Optional[EntityID], - implicit_sub_base_url: Optional[UssBaseURL] = None, - implicit_sub_for_constraints: Optional[bool] = None, + subscription_id: EntityID | None, + implicit_sub_base_url: UssBaseURL | None = None, + implicit_sub_for_constraints: bool | None = None, ) -> PutOperationalIntentReferenceParameters: """ Builds a PutOperationalIntentReferenceParameters object that can be used against the DSS OIR API. @@ -140,5 +139,5 @@ class PlanningAreaResource(Resource[PlanningAreaSpecification]): specification: PlanningAreaSpecification def __init__(self, specification: PlanningAreaSpecification, resource_origin: str): - super(PlanningAreaResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.specification = specification diff --git a/monitoring/uss_qualifier/resources/resource.py b/monitoring/uss_qualifier/resources/resource.py index 8673528f8f..1797ca1638 100644 --- a/monitoring/uss_qualifier/resources/resource.py +++ b/monitoring/uss_qualifier/resources/resource.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Dict, Generic, Set, Tuple, Type, TypeVar, get_type_hints +from typing import TypeVar, get_type_hints from implicitdict import ImplicitDict from loguru import logger @@ -15,7 +15,7 @@ SpecificationType = TypeVar("SpecificationType", bound=ImplicitDict) -class Resource(ABC, Generic[SpecificationType]): +class Resource[SpecificationType: ImplicitDict](ABC): resource_origin: str """The origin of this resource (usually the resource name in the top-level resource pool for a test configuration, though occasionally local to a test suite, derived from another resource, default, or something else)""" @@ -47,15 +47,15 @@ class MissingResourceError(ValueError): missing_resource_name: str def __init__(self, msg: str, missing_resource_name: str): - super(MissingResourceError, self).__init__(msg) + super().__init__(msg) self.missing_resource_name = missing_resource_name def create_resources( - resource_declarations: Dict[ResourceID, ResourceDeclaration], + resource_declarations: dict[ResourceID, ResourceDeclaration], resource_source: str, stop_when_not_created: bool = False, -) -> Dict[ResourceID, ResourceType]: +) -> dict[ResourceID, ResourceType]: """Instantiate all resources from the provided declarations. Note that some declarations, such as resources whose specifications contain ExternalFiles, may be mutated while the @@ -68,8 +68,8 @@ def create_resources( Returns: Mapping between resource ID and an instance of that declared resource. """ - resource_pool: Dict[ResourceID, ResourceType] = {} - could_not_create: Set[ResourceID] = set() + resource_pool: dict[ResourceID, ResourceType] = {} + could_not_create: set[ResourceID] = set() resources_created = 1 unmet_dependencies_by_resource = {} @@ -132,7 +132,7 @@ def create_resources( def get_resource_types( declaration: ResourceDeclaration, -) -> Tuple[Type[Resource], Type[ImplicitDict]]: +) -> tuple[type[Resource], type[ImplicitDict]]: """Get the resource and specification types from the declaration, validating against the resource's constructor signature. Args: @@ -152,9 +152,7 @@ def get_resource_types( ) if not issubclass(resource_type, Resource): raise NotImplementedError( - "Resource type {} is not a subclass of the Resource base class".format( - resource_type.__name__ - ) + f"Resource type {resource_type.__name__} is not a subclass of the Resource base class" ) constructor_signature = get_type_hints(resource_type.__init__) @@ -179,7 +177,7 @@ def get_resource_types( def _make_resource( declaration: ResourceDeclaration, - resource_pool: Dict[ResourceID, Resource], + resource_pool: dict[ResourceID, Resource], resource_origin: str, ) -> Resource: resource_type, specification_type = get_resource_types(declaration) @@ -188,9 +186,7 @@ def _make_resource( for arg_name, pool_source in declaration.dependencies.items(): if pool_source not in resource_pool: raise ValueError( - 'Resource "{}" was not found in the resource pool when trying to create {} resource'.format( - pool_source, declaration.resource_type - ) + f'Resource "{pool_source}" was not found in the resource pool when trying to create {declaration.resource_type} resource' ) constructor_args[arg_name] = resource_pool[pool_source] if specification_type is not None: @@ -204,11 +200,11 @@ def _make_resource( return resource -def make_child_resources( - parent_resources: Dict[ResourceID, ResourceType], - child_resource_map: Dict[ResourceID, ResourceID], +def make_child_resources[ResourceType: Resource]( + parent_resources: dict[ResourceID, ResourceType], + child_resource_map: dict[ResourceID, ResourceID], subject: str, -) -> Dict[ResourceID, ResourceType]: +) -> dict[ResourceID, ResourceType]: child_resources = {} for child_id, parent_id in child_resource_map.items(): is_optional = parent_id.endswith("?") diff --git a/monitoring/uss_qualifier/resources/versioning/client.py b/monitoring/uss_qualifier/resources/versioning/client.py index 13e4ee15c6..b5caea4c71 100644 --- a/monitoring/uss_qualifier/resources/versioning/client.py +++ b/monitoring/uss_qualifier/resources/versioning/client.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.versioning.constants import Scope @@ -20,7 +18,7 @@ class InterUSSVersionProvider(ImplicitDict): class VersionProviderSpecification(ImplicitDict): - interuss: Optional[InterUSSVersionProvider] = None + interuss: InterUSSVersionProvider | None = None """Populated when the version provider is using the InterUSS automated testing versioning API to provide versioning information.""" participant_id: ParticipantID @@ -28,11 +26,11 @@ class VersionProviderSpecification(ImplicitDict): class VersionProvidersSpecification(ImplicitDict): - instances: List[VersionProviderSpecification] + instances: list[VersionProviderSpecification] class VersionProvidersResource(Resource[VersionProvidersSpecification]): - version_providers: List[VersioningClient] + version_providers: list[VersioningClient] def __init__( self, @@ -40,7 +38,7 @@ def __init__( resource_origin: str, auth_adapter: AuthAdapterResource, ): - super(VersionProvidersResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) auth_adapter.assert_scopes_available( { Scope.ReadSystemVersions: "read and record the version of each system under test", diff --git a/monitoring/uss_qualifier/resources/versioning/system_identity.py b/monitoring/uss_qualifier/resources/versioning/system_identity.py index 80934b1d90..3592dcd7c0 100644 --- a/monitoring/uss_qualifier/resources/versioning/system_identity.py +++ b/monitoring/uss_qualifier/resources/versioning/system_identity.py @@ -14,5 +14,5 @@ class SystemIdentityResource(Resource[SystemIdentitySpecification]): def __init__( self, specification: SystemIdentitySpecification, resource_origin: str ): - super(SystemIdentityResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.system_identity = specification.system_identity diff --git a/monitoring/uss_qualifier/resources/vertices.py b/monitoring/uss_qualifier/resources/vertices.py index a41e1cd804..39a00a3fea 100644 --- a/monitoring/uss_qualifier/resources/vertices.py +++ b/monitoring/uss_qualifier/resources/vertices.py @@ -1,5 +1,3 @@ -from typing import List - from implicitdict import ImplicitDict from monitoring.monitorlib.geo import LatLngPoint @@ -10,7 +8,7 @@ class VerticesSpecification(ImplicitDict): """Specifies a list of vertices representing a 2D area. Useful for passing arbitrary areas to test scenarios.""" - vertices: List[LatLngPoint] + vertices: list[LatLngPoint] """Represents a 2D area""" @@ -18,5 +16,5 @@ class VerticesResource(Resource[VerticesSpecification]): specification: VerticesSpecification def __init__(self, specification: VerticesSpecification, resource_origin: str): - super(VerticesResource, self).__init__(specification, resource_origin) + super().__init__(specification, resource_origin) self.specification = specification diff --git a/monitoring/uss_qualifier/scenarios/astm/dss/datastore_access.py b/monitoring/uss_qualifier/scenarios/astm/dss/datastore_access.py index fda59751cd..d1d867d928 100644 --- a/monitoring/uss_qualifier/scenarios/astm/dss/datastore_access.py +++ b/monitoring/uss_qualifier/scenarios/astm/dss/datastore_access.py @@ -1,5 +1,3 @@ -from typing import List - from monitoring.uss_qualifier.resources.interuss.datastore import ( DatastoreDBClusterResource, DatastoreDBNode, @@ -9,7 +7,7 @@ class DatastoreAccess(GenericTestScenario): - datastore_nodes: List[DatastoreDBNode] = [] + datastore_nodes: list[DatastoreDBNode] = [] def __init__( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/aggregate_checks.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/aggregate_checks.py index c40b13e8a8..92aebae8e2 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/aggregate_checks.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/aggregate_checks.py @@ -1,5 +1,4 @@ import re -from typing import Dict, List, Optional from loguru import logger @@ -24,21 +23,21 @@ class AggregateChecks(GenericTestScenario): _rid_version: RIDVersion - _service_providers: List[NetRIDServiceProvider] - _observers: List[RIDSystemObserver] - _dss_instances: List[DSSInstance] + _service_providers: list[NetRIDServiceProvider] + _observers: list[RIDSystemObserver] + _dss_instances: list[DSSInstance] - _queries: List[fetch.Query] - _participants_by_base_url: Dict[str, ParticipantID] = dict() + _queries: list[fetch.Query] + _participants_by_base_url: dict[str, ParticipantID] = dict() _allow_cleartext_queries: bool = False - _queries_by_participant: Dict[ParticipantID, List[fetch.Query]] + _queries_by_participant: dict[ParticipantID, list[fetch.Query]] def __init__( self, service_providers: NetRIDServiceProviders, observers: NetRIDObserversResource, dss_instances: DSSInstancesResource, - test_exclusions: Optional[TestExclusionsResource] = None, + test_exclusions: TestExclusionsResource | None = None, ): super().__init__() self._service_providers = service_providers.service_providers @@ -159,7 +158,7 @@ def _verify_https_everywhere(self): logger.warning(msg) def _inspect_participant_queries( - self, participant_id: str, participant_queries: List[fetch.Query] + self, participant_id: str, participant_queries: list[fetch.Query] ): cleartext_queries = [] for query in participant_queries: @@ -190,7 +189,7 @@ def _dp_display_data_details_times_step(self): NetDpDetailsResponse95thPercentile (2s) and NetDpDetailsResponse99thPercentile (6s) """ for participant, all_queries in self._queries_by_participant.items(): - relevant_queries: List[fetch.Query] = list() + relevant_queries: list[fetch.Query] = list() for query in all_queries: if ( query.status_code == 200 @@ -233,7 +232,7 @@ def _dp_display_data_details_times_step(self): def _sp_flights_area_times_step(self): for participant, all_queries in self._queries_by_participant.items(): # identify successful flights queries - relevant_queries: List[fetch.Query] = list() + relevant_queries: list[fetch.Query] = list() for query in all_queries: if query.has_field_with_value("query_type") and ( # TODO find a cleaner way than checking for version here @@ -280,7 +279,7 @@ def _dp_display_data_times_step(self): pattern = re.compile(r"/display_data\?view=(-?\d+(.\d+)?,){3}-?\d+(.\d+)?") for participant, all_queries in self._queries_by_participant.items(): # identify successful display_data queries - relevant_queries: List[fetch.Query] = list() + relevant_queries: list[fetch.Query] = list() for query in all_queries: match = pattern.search(query.request.url) if match is not None and query.status_code == 200: diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dp_behavior.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dp_behavior.py index 250e116003..84ef0168f1 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dp_behavior.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dp_behavior.py @@ -1,6 +1,5 @@ import math from datetime import datetime, timedelta -from typing import List, Optional from urllib.parse import parse_qs, urlparse import arrow @@ -45,14 +44,14 @@ class DisplayProviderBehavior(GenericTestScenario): SUB_TYPE = register_resource_type(400, "ISA") - _observers: List[RIDSystemObserver] + _observers: list[RIDSystemObserver] _mock_uss: MockUSSClient - _dss_wrapper: Optional[DSSWrapper] + _dss_wrapper: DSSWrapper | None _isa_id: str - _isa_area: List[s2sphere.LatLng] + _isa_area: list[s2sphere.LatLng] - _identification: Optional[USSIdentificationResource] + _identification: USSIdentificationResource | None def __init__( self, @@ -61,7 +60,7 @@ def __init__( id_generator: IDGeneratorResource, # provides the ISA IS to be used dss_pool: DSSInstancesResource, isa: ServiceAreaResource, # area for which the ISA is created - uss_identification: Optional[USSIdentificationResource] = None, + uss_identification: USSIdentificationResource | None = None, ): super().__init__() self._observers = observers.observers diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/endpoint_encryption.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/endpoint_encryption.py index 803c1b6713..0f93ed6daf 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/endpoint_encryption.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/endpoint_encryption.py @@ -1,5 +1,4 @@ import errno -import socket from urllib.parse import urlparse import requests @@ -86,7 +85,7 @@ def _step_http_unavailable_or_redirect(self): pass except requests.exceptions.Timeout: pass - except socket.error as e: + except OSError as e: if e.errno not in [ errno.ECONNREFUSED, errno.ETIMEDOUT, diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/heavy_traffic_concurrent.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/heavy_traffic_concurrent.py index 5293fe39b3..3856c00a7b 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/heavy_traffic_concurrent.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/heavy_traffic_concurrent.py @@ -1,7 +1,6 @@ import asyncio import typing from datetime import UTC, datetime -from typing import Dict, List import aiohttp import arrow @@ -46,11 +45,11 @@ class HeavyTrafficConcurrent(GenericTestScenario): ISA_TYPE = register_resource_type(374, "ISA") - _isa_ids: List[str] + _isa_ids: list[str] - _isa_params: Dict[str, any] + _isa_params: dict[str, any] - _isa_versions: Dict[str, str] + _isa_versions: dict[str, str] _async_session: AsyncUTMTestSession @@ -66,7 +65,7 @@ def __init__( ) # TODO: delete once _delete_isa_if_exists updated to use dss_wrapper self._dss_wrapper = DSSWrapper(self, dss.dss_instance) - self._isa_versions: Dict[str, str] = {} + self._isa_versions: dict[str, str] = {} self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] @@ -152,7 +151,7 @@ def _get_isas_by_id_concurrent_step(self): asyncio.gather(*[self._get_isa(isa_id) for isa_id in self._isa_ids]) ) - results = typing.cast(Dict[str, FetchedISA], results) + results = typing.cast(dict[str, FetchedISA], results) for _, fetched_isa in results: self.record_query(fetched_isa.query) @@ -320,7 +319,7 @@ def _create_isas_concurrent_step(self): asyncio.gather(*[self._create_isa(isa_id) for isa_id in self._isa_ids]) ) - results = typing.cast(Dict[str, ChangedISA], results) + results = typing.cast(dict[str, ChangedISA], results) for _, fetched_isa in results: self.record_query(fetched_isa.query) @@ -402,7 +401,7 @@ def _delete_isas(self): ) ) - results = typing.cast(Dict[str, ChangedISA], results) + results = typing.cast(dict[str, ChangedISA], results) for _, fetched_isa in results: self.record_query(fetched_isa.query) @@ -436,7 +435,7 @@ def _get_deleted_isas(self): asyncio.gather(*[self._get_isa(isa_id) for isa_id in self._isa_ids]) ) - results = typing.cast(Dict[str, ChangedISA], results) + results = typing.cast(dict[str, ChangedISA], results) for _, fetched_isa in results: self.record_query(fetched_isa.query) diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_expiry.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_expiry.py index 25d56c6b91..c083866eb1 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_expiry.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_expiry.py @@ -1,5 +1,4 @@ import datetime -from typing import Optional import arrow @@ -35,7 +34,7 @@ def __init__( ) # TODO: delete once _delete_isa_if_exists updated to use dss_wrapper self._dss_wrapper = DSSWrapper(self, dss.dss_instance) self._isa_id = id_generator.id_factory.make_id(ISAExpiry.ISA_TYPE) - self._isa_version: Optional[str] = None + self._isa_version: str | None = None self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_simple.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_simple.py index c5652fd090..a7553e9181 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_simple.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_simple.py @@ -1,5 +1,4 @@ import datetime -from typing import List, Optional import arrow import s2sphere @@ -24,7 +23,7 @@ class ISASimple(GenericTestScenario): ISA_TYPE = register_resource_type(348, "ISA") - _huge_are: List[s2sphere.LatLng] + _huge_are: list[s2sphere.LatLng] def __init__( self, @@ -39,7 +38,7 @@ def __init__( ) # TODO: delete once _delete_isa_if_exists updated to use dss_wrapper self._dss_wrapper = DSSWrapper(self, dss.dss_instance) self._isa_id = id_generator.id_factory.make_id(ISASimple.ISA_TYPE) - self._isa_version: Optional[str] = None + self._isa_version: str | None = None self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] self._huge_area = [ diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_subscription_interactions.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_subscription_interactions.py index 5a8d4283e7..4bee611107 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_subscription_interactions.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_subscription_interactions.py @@ -1,5 +1,3 @@ -from typing import Optional - import arrow from monitoring.monitorlib import geo @@ -37,7 +35,7 @@ def __init__( ISASubscriptionInteractions.SUB_TYPE ) - self._isa_version: Optional[str] = None + self._isa_version: str | None = None self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] self._slight_overlap_area = geo.generate_slight_overlap_area(self._isa_area) diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validation.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validation.py index 21c57ea859..55cc0e5c81 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validation.py @@ -1,6 +1,6 @@ import copy import datetime -from typing import Any, Dict, List, Optional +from typing import Any import arrow import s2sphere @@ -25,7 +25,7 @@ class ISAValidation(GenericTestScenario): ISA_TYPE = register_resource_type(368, "ISA") - _huge_are: List[s2sphere.LatLng] + _huge_are: list[s2sphere.LatLng] create_isa_path: str @@ -44,7 +44,7 @@ def __init__( ) # TODO: delete once _delete_isa_if_exists updated to use dss_wrapper self._dss_wrapper = DSSWrapper(self, dss.dss_instance) self._isa_id = id_generator.id_factory.make_id(ISAValidation.ISA_TYPE) - self._isa_version: Optional[str] = None + self._isa_version: str | None = None self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] @@ -120,7 +120,7 @@ def _delete_isa_if_exists(self): ignore_base_url=self._isa.base_url, ) - def _isa_huge_area_check(self) -> (str, Dict[str, Any]): + def _isa_huge_area_check(self) -> (str, dict[str, Any]): """Returns the request's URL and json payload for subsequently re-using it. It is of the following form (note that v19 and v22a have slight differences): @@ -212,7 +212,7 @@ def _isa_start_time_after_time_end(self): ) def _isa_vertices_are_valid(self): - INVALID_VERTICES: List[s2sphere.LatLng] = [ + INVALID_VERTICES: list[s2sphere.LatLng] = [ s2sphere.LatLng.from_degrees(lat=130, lng=-23), s2sphere.LatLng.from_degrees(lat=130, lng=-24), s2sphere.LatLng.from_degrees(lat=132, lng=-24), @@ -235,7 +235,7 @@ def _isa_vertices_are_valid(self): isa_version=self._isa_version, ) - def _isa_missing_outline(self, create_isa_url: str, json_body: Dict[str, Any]): + def _isa_missing_outline(self, create_isa_url: str, json_body: dict[str, Any]): payload = copy.deepcopy(json_body) if self._dss.rid_version == RIDVersion.f3411_19: del payload["extents"]["spatial_volume"]["footprint"] @@ -269,7 +269,7 @@ def _isa_missing_outline(self, create_isa_url: str, json_body: Dict[str, Any]): required_status_code={400}, ) - def _isa_missing_volume(self, create_isa_url: str, json_body: Dict[str, Any]): + def _isa_missing_volume(self, create_isa_url: str, json_body: dict[str, Any]): payload = copy.deepcopy(json_body) if self._dss.rid_version == RIDVersion.f3411_19: del payload["extents"]["spatial_volume"] @@ -303,7 +303,7 @@ def _isa_missing_volume(self, create_isa_url: str, json_body: Dict[str, Any]): required_status_code={400}, ) - def _isa_missing_extents(self, create_isa_url: str, json_body: Dict[str, Any]): + def _isa_missing_extents(self, create_isa_url: str, json_body: dict[str, Any]): payload = copy.deepcopy(json_body) del payload["extents"] diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validator.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validator.py index 5e6cb328b1..3efe5cd64f 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/isa_validator.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Dict, List, Optional from monitoring.monitorlib import schema_validation from monitoring.monitorlib.fetch.rid import ISA, FetchedISA, FetchedISAs @@ -13,7 +12,7 @@ MAX_SKEW = 1e-6 # seconds maximum difference between expected and actual timestamps -class ISAValidator(object): +class ISAValidator: """Wraps the validation logic for an ISA that was returned by the DSS. It will compare the returned ISA with the parameters specified at its creation. """ @@ -21,16 +20,16 @@ class ISAValidator(object): _main_check: PendingCheck _scenario: GenericTestScenario # Params are optional: if they are not set, the field contents will not be checked - _isa_params: Optional[Dict[str, any]] - _dss_id: List[str] + _isa_params: dict[str, any] | None + _dss_id: list[str] _rid_version: RIDVersion def __init__( self, main_check: PendingCheck, scenario: GenericTestScenario, - isa_params: Optional[Dict[str, any]], - dss_id: List[str], + isa_params: dict[str, any] | None, + dss_id: list[str], rid_version: RIDVersion, ): self._main_check = main_check @@ -59,12 +58,10 @@ def _validate_isa( expected_isa_id: str, dss_isa: ISA, t_dss: datetime, - previous_version: Optional[ - str - ] = None, # If set, we control that the version changed - expected_version: Optional[ - str - ] = None, # If set, we control that the version has not changed + previous_version: str + | None = None, # If set, we control that the version changed + expected_version: str + | None = None, # If set, we control that the version has not changed ) -> None: isa_id = expected_isa_id dss_id = self._dss_id @@ -179,7 +176,7 @@ def validate_mutated_isa( self, expected_isa_id: str, mutated_isa: ChangedISA, - previous_version: Optional[str] = None, + previous_version: str | None = None, ): """ Validates the DSS reply to an ISA mutation request. @@ -243,7 +240,7 @@ def validate_deleted_isa( def validate_searched_isas( self, fetched_isas: FetchedISAs, - expected_versions: Dict[str, str], + expected_versions: dict[str, str], ): """Validates the DSS reply to an ISA search request: based on the ISA ID's present in expected_versions, it will verify the content of the returned ISA's. diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_simple.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_simple.py index 46b8b59964..09b0f5ed67 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_simple.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_simple.py @@ -1,6 +1,6 @@ import re from datetime import datetime, timedelta -from typing import Any, Dict, List +from typing import Any import s2sphere @@ -30,19 +30,19 @@ class SubscriptionSimple(GenericTestScenario): # The value for 'owner' we'll expect the DSS to set on subscriptions _client_identity: ClientIdentityResource - _test_subscription_ids: List[str] + _test_subscription_ids: list[str] # Base parameters used for subscription creation variations - _default_creation_params: Dict[str, Any] + _default_creation_params: dict[str, Any] # Effective parameters used for each subscription, indexed by subscription ID - _sub_params_by_sub_id: Dict[str, Dict[str, Any]] + _sub_params_by_sub_id: dict[str, dict[str, Any]] # Keep track of the latest subscription returned by the DSS - _current_subscriptions: Dict[str, Subscription] + _current_subscriptions: dict[str, Subscription] # An area designed to be too big to be allowed to search by the DSS - _problematically_big_area: List[s2sphere.LatLng] + _problematically_big_area: list[s2sphere.LatLng] def __init__( self, @@ -215,7 +215,7 @@ def _create_and_validate_subs(self): all_set_params["sub_id"] = self._test_subscription_ids[3] self._create_sub_with_params(all_set_params) - def _create_sub_with_params(self, creation_params: Dict[str, Any]): + def _create_sub_with_params(self, creation_params: dict[str, Any]): with self.check( "Create subscription", [self._dss_wrapper.participant_id] ) as check: @@ -254,7 +254,7 @@ def _compare_upsert_resp_with_params( self, sub_id: str, creation_resp_under_test: ChangedSubscription, - creation_params: Dict[str, Any], + creation_params: dict[str, Any], was_mutated: bool, ): """ @@ -517,9 +517,9 @@ def _validate_subscription_and_notif_index( self, sub_id: str, sub_under_test: Subscription, - creation_params: Dict[str, Any], + creation_params: dict[str, Any], was_mutated: bool, - query_timestamps: List[datetime], + query_timestamps: list[datetime], ): """Compare the passed subscription with the data we specified when creating it""" self._validate_subscription( @@ -548,9 +548,9 @@ def _validate_subscription( self, sub_id: str, sub_under_test: Subscription, - creation_params: Dict[str, Any], + creation_params: dict[str, Any], was_mutated: bool, - query_timestamps: List[datetime], + query_timestamps: list[datetime], ): """ Validate the subscription against the parameters used to create it. diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_validation.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_validation.py index d0b2de898b..0b107a6a97 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/subscription_validation.py @@ -1,5 +1,4 @@ import datetime -from typing import Dict from monitoring.monitorlib.mutate.rid import ChangedSubscription from monitoring.prober.infrastructure import register_resource_type @@ -210,7 +209,7 @@ def _check_properly_truncated( # If a subscription was created, we want to delete it before continuing: self._dss_wrapper.cleanup_sub(sub_id=self._sub_id) - def _default_subscription_params(self, duration: datetime.timedelta) -> Dict: + def _default_subscription_params(self, duration: datetime.timedelta) -> dict: now = datetime.datetime.now(datetime.UTC) return dict( area_vertices=[vertex.as_s2sphere() for vertex in self._isa.footprint], diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/token_validation.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/token_validation.py index c09d476297..7d164fe751 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/token_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/token_validation.py @@ -1,5 +1,4 @@ import datetime -from typing import Dict, List, Optional import arrow import s2sphere @@ -44,7 +43,7 @@ def __init__( self._dss = dss.dss_instance self._dss_wrapper = DSSWrapper(self, dss.dss_instance) self._isa_id = id_generator.id_factory.make_id(ISASimple.ISA_TYPE) - self._isa_version: Optional[str] = None + self._isa_version: str | None = None self._isa = isa.specification self._isa_area = [vertex.as_s2sphere() for vertex in self._isa.footprint] @@ -407,7 +406,7 @@ def _delete_isa_if_exists(self): ) self._verify_notifications(deleted.notifications) - def _verify_notifications(self, notifications: Dict[str, ISAChangeNotification]): + def _verify_notifications(self, notifications: dict[str, ISAChangeNotification]): for subscriber_url, notification in notifications.items(): pid = ( notification.query.participant_id @@ -425,7 +424,7 @@ def _verify_notifications(self, notifications: Dict[str, ISAChangeNotification]) def _put_isa_tweak_auth( self, utm_client: infrastructure.UTMClientSession, - isa_version: Optional[str] = None, + isa_version: str | None = None, scope_intent: str = "read", ) -> ISAChange: """A local version of mutate.rid.put_isa that lets us control authentication parameters""" @@ -621,9 +620,9 @@ def _del_isa_tweak_auth( def _search_isas_tweak_auth( self, utm_client: infrastructure.UTMClientSession, - area: List[s2sphere.LatLng], - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + area: list[s2sphere.LatLng], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, ) -> FetchedISAs: url_time_params = "" if start_time is not None: @@ -680,7 +679,7 @@ def _search_isas_tweak_auth( def _query_scope_for_auth_params( self, utm_client: infrastructure.UTMClientSession, scope_intent: str - ) -> Optional[str]: + ) -> str | None: if utm_client.auth_adapter is not None: if self._dss.rid_version == RIDVersion.f3411_19: if scope_intent == "read": diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/utils.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/utils.py index 4975829685..402a624af7 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/utils.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss/utils.py @@ -1,5 +1,3 @@ -from typing import Optional - from monitoring.monitorlib.fetch import rid as fetch from monitoring.monitorlib.infrastructure import UTMClientSession from monitoring.monitorlib.mutate import rid as mutate @@ -12,8 +10,8 @@ def delete_isa_if_exists( isa_id: str, rid_version: RIDVersion, session: UTMClientSession, - participant_id: Optional[str] = None, - ignore_base_url: Optional[str] = None, + participant_id: str | None = None, + ignore_base_url: str | None = None, ): """ Deletes an ISA from the DSS that lives behind the provided session, and takes diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss_interoperability.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss_interoperability.py index 4a58c989b6..4b5b950423 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss_interoperability.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/dss_interoperability.py @@ -4,7 +4,6 @@ import uuid from dataclasses import dataclass from enum import Enum -from typing import Dict, List, Optional from urllib.parse import urlparse import s2sphere @@ -37,11 +36,11 @@ class EntityType(str, Enum): @dataclass -class TestEntity(object): +class TestEntity: type: EntityType uuid: str - version: Optional[str] = None - creation_params: Optional[Dict] = None + version: str | None = None + creation_params: dict | None = None class DSSInteroperability(GenericTestScenario): @@ -52,10 +51,10 @@ class DSSInteroperability(GenericTestScenario): """ _dss_primary: DSSWrapper - _dss_others: List[DSSWrapper] + _dss_others: list[DSSWrapper] _allow_private_addresses: bool = False - _context: Dict[str, TestEntity] - _area_vertices: List[s2sphere.LatLng] + _context: dict[str, TestEntity] + _area_vertices: list[s2sphere.LatLng] _planning_area: PlanningAreaSpecification def __init__( @@ -63,7 +62,7 @@ def __init__( primary_dss_instance: DSSInstanceResource, all_dss_instances: DSSInstancesResource, planning_area: PlanningAreaResource, - test_exclusions: Optional[TestExclusionsResource] = None, + test_exclusions: TestExclusionsResource | None = None, ): super().__init__() self._dss_primary = DSSWrapper(self, primary_dss_instance.dss_instance) @@ -80,7 +79,7 @@ def __init__( if test_exclusions is not None: self._allow_private_addresses = test_exclusions.allow_private_addresses - self._context: Dict[str, TestEntity] = {} + self._context: dict[str, TestEntity] = {} # TODO migrate to ID generator? def _new_isa(self, name: str) -> TestEntity: @@ -92,7 +91,7 @@ def _new_sub(self, name: str) -> TestEntity: self._context[name] = TestEntity(EntityType.Sub, str(uuid.uuid4())) return self._context[name] - def _get_entities_by_prefix(self, prefix: str) -> Dict[str, TestEntity]: + def _get_entities_by_prefix(self, prefix: str) -> dict[str, TestEntity]: all_entities = dict() for name, entity in self._context.items(): if name.startswith(prefix): @@ -181,7 +180,7 @@ def step2(self): with self.check( "service_areas includes ISA from S1", [dss.participant_id] ) as check: - sub_isa: Optional[ISA] = next( + sub_isa: ISA | None = next( filter(lambda isa: isa.id == isa_1.uuid, created_sub.isas), None ) @@ -815,7 +814,7 @@ def step17(self): check, sub_id=sub_3.uuid, sub_version=sub_3.version ) - def _default_params(self, duration: datetime.timedelta) -> Dict: + def _default_params(self, duration: datetime.timedelta) -> dict: now = datetime.datetime.now().astimezone() return dict( diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/misbehavior.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/misbehavior.py index a50b801e31..dbc732fc6e 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/misbehavior.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/misbehavior.py @@ -1,4 +1,5 @@ -from typing import Callable, List, Set, Unpack +from collections.abc import Callable +from typing import Unpack import s2sphere from requests.exceptions import RequestException @@ -46,8 +47,8 @@ class Misbehavior(GenericTestScenario): _service_providers: NetRIDServiceProviders _evaluation_configuration: EvaluationConfigurationResource - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] def __init__( self, @@ -135,9 +136,9 @@ def _inject_flights(self): def _poll_during_flights( self, - diagonals_m: List[float], + diagonals_m: list[float], evaluation_func: Callable[ - [LatLngRect, Unpack[dict[str, auth.AuthAdapter | str]]], Set[str] + [LatLngRect, Unpack[dict[str, auth.AuthAdapter | str]]], set[str] ], evaluation_kwargs: dict[str, auth.AuthAdapter | str], ): @@ -203,7 +204,7 @@ def _fetch_flights_from_dss(self, rect: LatLngRect) -> dict[str, TelemetryMappin def _evaluate_and_test_too_large_area_requests( self, rect: LatLngRect, - ) -> Set[str]: + ) -> set[str]: """Queries all flights from the DSS to discover flights urls and query them using a larger area than allowed. :returns: set of injection IDs that were encountered and tested @@ -258,7 +259,7 @@ def _evaluate_and_test_authentication( auth: auth.AuthAdapter, check_name: str, credentials_type_description: str, - ) -> Set[str]: + ) -> set[str]: """Queries all flights in the expected way, then repeats the queries to SPs without credentials. returns true once queries to SPS have been made without credentials. False otherwise, such as when diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/networked_uas_disconnect.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/networked_uas_disconnect.py index 20c173189f..da37eade9d 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/networked_uas_disconnect.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/networked_uas_disconnect.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import List from requests.exceptions import RequestException from s2sphere import LatLngRect @@ -37,8 +36,8 @@ class NetworkedUASDisconnect(GenericTestScenario): _service_providers: NetRIDServiceProviders _evaluation_configuration: EvaluationConfigurationResource - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] def __init__( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/nominal_behavior.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/nominal_behavior.py index d474fd182a..0771221a9a 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/nominal_behavior.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/nominal_behavior.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from requests.exceptions import RequestException from s2sphere import LatLngRect @@ -36,8 +34,8 @@ class NominalBehavior(GenericTestScenario): _observers: NetRIDObserversResource _evaluation_configuration: EvaluationConfigurationResource - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] def __init__( self, @@ -45,7 +43,7 @@ def __init__( service_providers: NetRIDServiceProviders, observers: NetRIDObserversResource, evaluation_configuration: EvaluationConfigurationResource, - dss_pool: Optional[DSSInstancesResource] = None, + dss_pool: DSSInstancesResource | None = None, ): super().__init__() self._flights_data = flights_data diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_notification_behavior.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_notification_behavior.py index 1a9ab33663..cef5518948 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_notification_behavior.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_notification_behavior.py @@ -1,5 +1,6 @@ +from collections.abc import Callable from datetime import timedelta -from typing import Callable, Dict, List, Optional, Set, Type, TypeVar, Union +from typing import TypeVar from future.backports.datetime import datetime from implicitdict import ImplicitDict @@ -49,10 +50,10 @@ class ServiceProviderNotificationBehavior(GenericTestScenario): _service_providers: NetRIDServiceProviders _mock_uss: MockUSSClient - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] - _dss_wrapper: Optional[DSSWrapper] + _dss_wrapper: DSSWrapper | None _subscription_id: str def __init__( @@ -112,7 +113,7 @@ def run(self, context: ExecutionContext): def _subscribe_mock_uss(self): # Get all bounding rects for flights flight_rects = [f.get_rect() for f in self._flights_data.get_test_flights()] - flight_union: Optional[LatLngRect] = None + flight_union: LatLngRect | None = None for fr in flight_rects: if flight_union is None: flight_union = fr @@ -159,7 +160,7 @@ def post_isa_filter(interaction: Interaction): in interaction.query.request.url ) - def fetch_interactions() -> List[Interaction]: + def fetch_interactions() -> list[Interaction]: return get_mock_uss_interactions( self, self._mock_uss, @@ -169,7 +170,7 @@ def fetch_interactions() -> List[Interaction]: post_isa_filter, )[0] - def includes_all_notifications(raw_interactions: List[Interaction]) -> bool: + def includes_all_notifications(raw_interactions: list[Interaction]) -> bool: pids_having_notified = self._relevant_notified_subs( raw_interactions, relevant_participant_ids, notifications_received_after ) @@ -209,7 +210,7 @@ def includes_all_notifications(raw_interactions: List[Interaction]) -> bool: ) continue - def _notif_operation_id(self) -> Union[api_v19.OperationID | api_v22a.OperationID]: + def _notif_operation_id(self) -> api_v19.OperationID | api_v22a.OperationID: if self._rid_version.f3411_19: return api_v19.OperationID.PostIdentificationServiceArea elif self._rid_version.f3411_22a: @@ -219,7 +220,7 @@ def _notif_operation_id(self) -> Union[api_v19.OperationID | api_v22a.OperationI def _notif_param_type( self, - ) -> Type[ + ) -> type[ api_v19.PutIdentificationServiceAreaNotificationParameters | api_v22a.PutIdentificationServiceAreaNotificationParameters ]: @@ -232,10 +233,10 @@ def _notif_param_type( def _relevant_notified_subs( self, - raw_interactions: List[Interaction], - relevant_pids: Set[str], + raw_interactions: list[Interaction], + relevant_pids: set[str], received_after: datetime, - ) -> Dict[str, List[datetime]]: + ) -> dict[str, list[datetime]]: # Parse version-specific notification parameters PutIsaParamsType = self._notif_param_type() @@ -256,7 +257,7 @@ def _relevant_notified_subs( ): relevant.append((received_at, notification.service_area.owner)) - notifs_by_participant: Dict[str, List[datetime]] = {} + notifs_by_participant: dict[str, list[datetime]] = {} for received_at, participant_id in relevant: if participant_id not in notifs_by_participant: notifs_by_participant[participant_id] = [] diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_missing_fields.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_missing_fields.py index 62420de929..1905fa484f 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_missing_fields.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_missing_fields.py @@ -1,5 +1,3 @@ -from typing import List - import arrow from requests.exceptions import RequestException from s2sphere import LatLngRect @@ -38,8 +36,8 @@ class SpOperatorNotifyMissingFields(GenericTestScenario): _service_providers: NetRIDServiceProviders _evaluation_configuration: EvaluationConfigurationResource - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] def __init__( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_slow_update.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_slow_update.py index cfc349c136..9ed86a8624 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_slow_update.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common/sp_operator_notify_slow_update.py @@ -1,5 +1,4 @@ import datetime -from typing import List import arrow from future.backports.datetime import timedelta @@ -39,10 +38,10 @@ class ServiceProviderNotifiesSlowUpdates(GenericTestScenario): _service_providers: NetRIDServiceProviders _evaluation_configuration: EvaluationConfigurationResource - _injected_flights: List[InjectedFlight] - _injected_tests: List[InjectedTest] + _injected_flights: list[InjectedFlight] + _injected_tests: list[InjectedTest] - _pre_injection_notifications: dict[str, List[str]] = {} + _pre_injection_notifications: dict[str, list[str]] = {} _notifications_before: datetime _notifications_after: datetime diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator.py index 144eeee951..266f8b9b06 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator.py @@ -5,7 +5,7 @@ import re import uuid from collections.abc import Callable -from typing import Any, List, Optional, TypeVar, Union +from typing import Any, TypeVar from arrow import ParserError from implicitdict import StringBasedDateTime @@ -47,7 +47,7 @@ T2 = TypeVar("T2") -class RIDCommonDictionaryEvaluator(object): +class RIDCommonDictionaryEvaluator: flight_evaluators = [ "_evaluate_ua_type", ] @@ -118,7 +118,7 @@ def evaluate_dp_flight( injected_telemetry: injection.RIDAircraftState, injected_flight: injection.TestFlight, observed_flight: observation_api.Flight, - participants: List[str], + participants: list[str], query_timestamp: datetime.datetime, ): """Implements fragment documented in `common_dictionary_evaluator_dp_flight.md`.""" @@ -182,7 +182,7 @@ def evaluate_sp_details( def evaluate_dp_details( self, injected_details: injection.RIDFlightDetails, - observed_details: Optional[observation_api.GetDetailsResponse], + observed_details: observation_api.GetDetailsResponse | None, participant_id: ParticipantID, query_timestamp: datetime.datetime, ): @@ -224,8 +224,8 @@ def evaluate_dp_details( def _evaluate_uas_id( self, injected: injection.RIDFlightDetails, # unused but required by callers - sp_observed: Optional[FlightDetails], - dp_observed: Optional[observation_api.GetDetailsResponse], + sp_observed: FlightDetails | None, + dp_observed: observation_api.GetDetailsResponse | None, participant: ParticipantID, query_timestamp: datetime.datetime, ): @@ -265,7 +265,7 @@ def _evaluate_uas_id( def _evaluate_uas_id_serial_number( self, - dp_observed: Optional[observation_api.GetDetailsResponse], + dp_observed: observation_api.GetDetailsResponse | None, **generic_kwargs, ): """ @@ -276,9 +276,7 @@ def _evaluate_uas_id_serial_number( ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def skip_eval( - injected_val: Optional[str], observed_val: Optional[str] - ) -> Optional[str]: + def skip_eval(injected_val: str | None, observed_val: str | None) -> str | None: if not injected_val: return "Injected UAS ID is not 'Serial Number' type" @@ -298,9 +296,7 @@ def value_validator(val: SerialNumber) -> SerialNumber: return serial_number - def value_comparator( - v1: Optional[SerialNumber], v2: Optional[SerialNumber] - ) -> bool: + def value_comparator(v1: SerialNumber | None, v2: SerialNumber | None) -> bool: return v1 == v2 # NB: We don't use the two redundant fields `serial_number` and `uas_id.serial_number`, @@ -322,7 +318,7 @@ def value_comparator( def _evaluate_uas_id_registration_id( self, - dp_observed: Optional[observation_api.GetDetailsResponse], + dp_observed: observation_api.GetDetailsResponse | None, **generic_kwargs, ): """ @@ -333,9 +329,7 @@ def _evaluate_uas_id_registration_id( ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def skip_eval( - injected_val: Optional[str], observed_val: Optional[str] - ) -> Optional[str]: + def skip_eval(injected_val: str | None, observed_val: str | None) -> str | None: if not injected_val: return "Injected UAS ID is not 'Registration ID' type" @@ -358,7 +352,7 @@ def value_validator(val: str) -> str: return val - def value_comparator(v1: Optional[str], v2: Optional[str]) -> bool: + def value_comparator(v1: str | None, v2: str | None) -> bool: return v1 == v2 # NB: We don't use the two redundant fields `registration_id` and `uas_id.registration_id`, @@ -380,7 +374,7 @@ def value_comparator(v1: Optional[str], v2: Optional[str]) -> bool: def _evaluate_uas_id_utm_id( self, - dp_observed: Optional[observation_api.GetDetailsResponse], + dp_observed: observation_api.GetDetailsResponse | None, **generic_kwargs, ): """ @@ -391,9 +385,7 @@ def _evaluate_uas_id_utm_id( ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def skip_eval( - injected_val: Optional[str], observed_val: Optional[str] - ) -> Optional[str]: + def skip_eval(injected_val: str | None, observed_val: str | None) -> str | None: if self._rid_version == RIDVersion.f3411_19: return f"Unsupported version {self._rid_version}" @@ -413,7 +405,7 @@ def value_validator(val: str) -> uuid.UUID: # We do accept the most common ones return uuid.UUID(val) - def value_comparator(v1: Optional[uuid.UUID], v2: Optional[uuid.UUID]) -> bool: + def value_comparator(v1: uuid.UUID | None, v2: uuid.UUID | None) -> bool: if not v1: # If we didn't inject a value, the UTM (in our case, the USS) may provide a value return True return v1 == v2 @@ -437,7 +429,7 @@ def value_comparator(v1: Optional[uuid.UUID], v2: Optional[uuid.UUID]) -> bool: def _evaluate_uas_id_specific_session_id( self, - dp_observed: Optional[observation_api.GetDetailsResponse], + dp_observed: observation_api.GetDetailsResponse | None, **generic_kwargs, ): """ @@ -448,9 +440,7 @@ def _evaluate_uas_id_specific_session_id( ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def skip_eval( - injected_val: Optional[str], observed_val: Optional[str] - ) -> Optional[str]: + def skip_eval(injected_val: str | None, observed_val: str | None) -> str | None: if self._rid_version == RIDVersion.f3411_19: return f"Unsupported version {self._rid_version}" @@ -468,7 +458,7 @@ def value_validator(val: str) -> str: # representation ?) so we don't perform any validation return val - def value_comparator(v1: Optional[str], v2: Optional[str]) -> bool: + def value_comparator(v1: str | None, v2: str | None) -> bool: return v1 == v2 # NB: We don't use the two redundant fields `specific_session_id` and `uas_id.specific_session_id`, @@ -517,7 +507,7 @@ def value_validator(val: str | Time) -> StringBasedDateTime: return val def value_comparator( - v1: Optional[StringBasedDateTime], v2: Optional[StringBasedDateTime] + v1: StringBasedDateTime | None, v2: StringBasedDateTime | None ) -> bool: if v1 is None or v2 is None: return False @@ -541,9 +531,9 @@ def value_comparator( def _evaluate_operator_id( self, - value_inj: Optional[str], - value_obs: Optional[str], - participants: List[str], + value_inj: str | None, + value_obs: str | None, + participants: list[str], ): if self._rid_version == RIDVersion.f3411_22a: if value_obs: @@ -591,7 +581,7 @@ def value_validator(val: float) -> float: raise ValueError(f"Speed is greater than {constants.MaxSpeed}") return val - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -634,7 +624,7 @@ def value_validator(val: float) -> float: return val - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -664,7 +654,7 @@ def _evaluate_position( self, position_inj: RIDAircraftPosition, position_obs: Position, - participants: List[str], + participants: list[str], ): if self._rid_version == RIDVersion.f3411_22a: with self._test_scenario.check( @@ -714,7 +704,7 @@ def _evaluate_height(self, **generic_kwargs): ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -746,7 +736,7 @@ def value_validator(val: str) -> RIDHeightReference: return RIDHeightReference(val) def value_comparator( - v1: Optional[RIDHeightReference], v2: Optional[RIDHeightReference] + v1: RIDHeightReference | None, v2: RIDHeightReference | None ) -> bool: return v1 == v2 @@ -769,13 +759,13 @@ def value_comparator( def _evaluate_operator_location( self, - position_inj: Optional[LatLngPoint], - altitude_inj: Optional[Altitude], - altitude_type_inj: Optional[injection.OperatorAltitudeAltitudeType], - position_obs: Optional[LatLngPoint], - altitude_obs: Optional[Altitude], - altitude_type_obs: Optional[observation_api.OperatorAltitudeAltitudeType], - participants: List[str], + position_inj: LatLngPoint | None, + altitude_inj: Altitude | None, + altitude_type_inj: injection.OperatorAltitudeAltitudeType | None, + position_obs: LatLngPoint | None, + altitude_obs: Altitude | None, + altitude_type_obs: observation_api.OperatorAltitudeAltitudeType | None, + participants: list[str], ): if self._rid_version == RIDVersion.f3411_22a: if not position_obs: @@ -903,8 +893,8 @@ def value_validator(val: str) -> RIDOperationalStatusv19 | RIDOperationalStatus: return RIDOperationalStatus(val) def value_comparator( - v1: Optional[RIDOperationalStatusv19 | RIDOperationalStatus], - v2: Optional[RIDOperationalStatusv19 | RIDOperationalStatus], + v1: RIDOperationalStatusv19 | RIDOperationalStatus | None, + v2: RIDOperationalStatusv19 | RIDOperationalStatus | None, ) -> bool: return v1 == v2 @@ -953,7 +943,7 @@ def observed_value_validator(check, observed_val: injection.UAType): ) def value_comparator( - v1: Optional[injection.UAType], v2: Optional[injection.UAType] + v1: injection.UAType | None, v2: injection.UAType | None ) -> bool: equivalent = {injection.UAType.HybridLift, injection.UAType.VTOL} @@ -990,7 +980,7 @@ def value_validator(val: float) -> float: raise ValueError("Timestamp accurary is less than 0") return val - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -1020,7 +1010,7 @@ def _evaluate_alt(self, **generic_kwargs): ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -1052,7 +1042,7 @@ def value_validator(val: VerticalAccuracy) -> VerticalAccuracy: return VerticalAccuracy(val) def value_comparator( - v1: Optional[VerticalAccuracy], v2: Optional[VerticalAccuracy] + v1: VerticalAccuracy | None, v2: VerticalAccuracy | None ) -> bool: if v1 is None or v2 is None: return False @@ -1085,7 +1075,7 @@ def value_validator(val: HorizontalAccuracy) -> HorizontalAccuracy: return HorizontalAccuracy(val) def value_comparator( - v1: Optional[HorizontalAccuracy], v2: Optional[HorizontalAccuracy] + v1: HorizontalAccuracy | None, v2: HorizontalAccuracy | None ) -> bool: if v1 is None or v2 is None: return False @@ -1118,7 +1108,7 @@ def value_validator(val: SpeedAccuracy) -> SpeedAccuracy: return SpeedAccuracy(val) def value_comparator( - v1: Optional[SpeedAccuracy], v2: Optional[SpeedAccuracy] + v1: SpeedAccuracy | None, v2: SpeedAccuracy | None ) -> bool: if v1 is None or v2 is None: return False @@ -1163,7 +1153,7 @@ def value_validator(val: float) -> float: ) return val - def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: + def value_comparator(v1: float | None, v2: float | None) -> bool: if v1 is None or v2 is None: return False @@ -1185,8 +1175,8 @@ def value_comparator(v1: Optional[float], v2: Optional[float]) -> bool: def _evaluate_ua_classification( self, injected: injection.RIDFlightDetails, - sp_observed: Optional[FlightDetails], - dp_observed: Optional[observation_api.GetDetailsResponse], + sp_observed: FlightDetails | None, + dp_observed: observation_api.GetDetailsResponse | None, participant: ParticipantID, query_timestamp: datetime.datetime, ): @@ -1208,11 +1198,11 @@ def _evaluate_ua_classification( ) return - injected_ua_classification: Optional[str] = None + injected_ua_classification: str | None = None if "eu_classification" in injected: injected_ua_classification = "eu_classification" - observed_ua_classification: Optional[str] = None + observed_ua_classification: str | None = None if sp_observed is not None: if sp_observed.raw.has_field_with_value("eu_classification"): observed_ua_classification = "eu_classification" @@ -1250,9 +1240,9 @@ def _evaluate_ua_classification_eu_category( """ def skip_eval( - _: Optional[UAClassificationEUCategory], - __: Optional[UAClassificationEUCategory], - ) -> Optional[str]: + _: UAClassificationEUCategory | None, + __: UAClassificationEUCategory | None, + ) -> str | None: if self._rid_version == RIDVersion.f3411_19: return f"Unsupported version {self._rid_version}" @@ -1267,8 +1257,8 @@ def cat_value_validator( return UAClassificationEUCategory(val) def cat_value_comparator( - v1: Optional[UAClassificationEUCategory], - v2: Optional[UAClassificationEUCategory], + v1: UAClassificationEUCategory | None, + v2: UAClassificationEUCategory | None, ) -> bool: if v1 is None or v2 is None: return False @@ -1302,9 +1292,9 @@ def _evaluate_ua_classification_eu_class( """ def skip_eval( - _: Optional[UAClassificationEUCategory], - __: Optional[UAClassificationEUCategory], - ) -> Optional[str]: + _: UAClassificationEUCategory | None, + __: UAClassificationEUCategory | None, + ) -> str | None: if self._rid_version == RIDVersion.f3411_19: return f"Unsupported version {self._rid_version}" @@ -1319,7 +1309,7 @@ def class_value_validator( return UAClassificationEUClass(val) def class_value_comparator( - v1: Optional[UAClassificationEUClass], v2: Optional[UAClassificationEUClass] + v1: UAClassificationEUClass | None, v2: UAClassificationEUClass | None ) -> bool: if v1 is None or v2 is None: return False @@ -1347,24 +1337,20 @@ def _generic_evaluator( sp_field_name: str, dp_field_name: str, field_human_name: str, - value_validator: Optional[Callable[[T], T2]], - observed_value_validator: Optional[Callable[[PendingCheck, T2], None]], + value_validator: Callable[[T], T2] | None, + observed_value_validator: Callable[[PendingCheck, T2], None] | None, injection_required_field: bool, - unknown_value: Optional[T2 | List[T2]], - value_comparator: Callable[[Optional[T2], Optional[T2]], bool], - injected: Union[ - injection.TestFlight, - injection.RIDAircraftState, - injection.RIDFlightDetails, - ], - sp_observed: Optional[Union[Flight, FlightDetails]], - dp_observed: Optional[ - Union[observation_api.Flight, observation_api.GetDetailsResponse] - ], + unknown_value: T2 | list[T2] | None, + value_comparator: Callable[[T2 | None, T2 | None], bool], + injected: injection.TestFlight + | injection.RIDAircraftState + | injection.RIDFlightDetails, + sp_observed: Flight | FlightDetails | None, + dp_observed: observation_api.Flight | observation_api.GetDetailsResponse | None, participant: ParticipantID, query_timestamp: datetime.datetime, sp_is_allowed_to_generate_missing: bool = False, - skip_eval: Optional[Callable[[Optional[T], Optional[T]], str]] = None, + skip_eval: Callable[[T | None, T | None], str] | None = None, ): """ Generic evaluator of a field. Exactly one of sp_observed or dp_observed must be provided. @@ -1396,7 +1382,7 @@ def _generic_evaluator( ValueError: if a test operation wasn't performed correctly by uss_qualifier. """ - injected_val: Optional[T] = _dotted_get(injected, injected_field_name) + injected_val: T | None = _dotted_get(injected, injected_field_name) if injected_val is not None: if value_validator is not None: try: @@ -1406,7 +1392,7 @@ def _generic_evaluator( f"Invalid {field_human_name} {injected_val} injected", e ) - observed_val: Optional[T] + observed_val: T | None if sp_observed is not None: observed_val = _dotted_get(sp_observed, sp_field_name) elif dp_observed is not None: @@ -1501,7 +1487,7 @@ def _generic_evaluator( ) -def _dotted_get(obj: Any, key: str) -> Optional[T]: +def _dotted_get(obj: Any, key: str) -> T | None: val: Any = obj for k in key.split("."): if val is None: diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator_test.py b/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator_test.py index ff6df5cfdf..25be87ff62 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator_test.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/common_dictionary_evaluator_test.py @@ -1,8 +1,9 @@ import uuid +from collections.abc import Callable from dataclasses import dataclass from datetime import datetime, timedelta from itertools import permutations -from typing import Any, Callable, List, Optional, Tuple, TypeVar +from typing import Any, TypeVar from implicitdict import ImplicitDict from uas_standards.ansi_cta_2063_a import SerialNumber @@ -92,14 +93,14 @@ def step_under_test(self: UnitTestScenario): def test_operator_location(): - valid_locations: List[ - Tuple[ - Optional[LatLngPoint], - Optional[Altitude], - Optional[OperatorAltitudeAltitudeType], - Optional[LatLngPoint], - Optional[Altitude], - Optional[OperatorAltitudeAltitudeType], + valid_locations: list[ + tuple[ + LatLngPoint | None, + Altitude | None, + OperatorAltitudeAltitudeType | None, + LatLngPoint | None, + Altitude | None, + OperatorAltitudeAltitudeType | None, int, ] ] = [ @@ -140,11 +141,11 @@ def test_operator_location(): for valid_location in valid_locations: _assert_operator_location(*valid_location, 0) - invalid_locations: List[ - Tuple[ - Optional[LatLngPoint], - Optional[Altitude], - Optional[OperatorAltitudeAltitudeType], + invalid_locations: list[ + tuple[ + LatLngPoint | None, + Altitude | None, + OperatorAltitudeAltitudeType | None, int, int, ] @@ -250,7 +251,7 @@ def mock_flight( last_position_time: datetime, positions_count: int, positions_time_delta_s: int, - position: Tuple[int, int] = (1.0, 1.0), + position: tuple[int, int] = (1.0, 1.0), ) -> Flight: v22a_flight = v22a.api.RIDFlight( id="flightId", @@ -269,8 +270,8 @@ def mock_positions( last_time: datetime, amount: int, positions_time_delta_s: int, - position: Tuple[int, int] = (1.0, 1.0), -) -> List[v22a.api.RIDRecentAircraftPosition]: + position: tuple[int, int] = (1.0, 1.0), +) -> list[v22a.api.RIDRecentAircraftPosition]: """generate a list of positions with the last one at last_time and the next ones going back in time by 10 seconds""" return [ v22a.api.RIDRecentAircraftPosition( @@ -286,7 +287,7 @@ def mock_positions( def to_positions( - coords: List[Tuple[float, float]], + coords: list[tuple[float, float]], first_time: datetime, positions_time_delta_s: int = 1, ) -> v22a.api.RIDRecentAircraftPosition: @@ -500,8 +501,8 @@ def _assert_generic_evaluator_call( sp_observed: Any, dp_observed: Any, outcome: bool, - rid_version: Optional[RIDVersion] = RIDVersion.f3411_22a, - wanted_fail: Optional[list[str]] = None, + rid_version: RIDVersion | None = RIDVersion.f3411_22a, + wanted_fail: list[str] | None = None, ): """ Verify that the 'fct' function on the RIDCommonDictionaryEvaluator is returning the expected result. @@ -569,8 +570,8 @@ def _assert_generic_evaluator_result( fct: str, *setters_and_values: list[Any], outcome: bool, - wanted_fail: Optional[list[str]] = None, - rid_version: Optional[RIDVersion] = None, + wanted_fail: list[str] | None = None, + rid_version: RIDVersion | None = None, ): """ Helper to call _assert_generic_evaluator_call that build mocked objects first and do the call. @@ -653,10 +654,10 @@ def _assert_generic_evaluator_correct_field_is_used( ) -def _assert_generic_evaluator_valid_value( +def _assert_generic_evaluator_valid_value[T]( *fct_and_setters: list[Any], valid_value: T, - rid_version: Optional[RIDVersion] = None, + rid_version: RIDVersion | None = None, ): """ Test that a _evaluate function is handeling a specifc value as valid. @@ -687,7 +688,7 @@ def _assert_generic_evaluator_invalid_value( *fct_and_setters: list[Any], invalid_value: T, valid_value: T, - rid_version: Optional[RIDVersion] = None, + rid_version: RIDVersion | None = None, ): """ Test that a _evaluate function is handeling a specifc value as invalid. @@ -730,10 +731,10 @@ def _assert_generic_evaluator_invalid_value( ) -def _assert_generic_evaluator_invalid_observed_value( +def _assert_generic_evaluator_invalid_observed_value[T]( *fct_and_setters: list[Any], invalid_value: T, - rid_version: Optional[RIDVersion] = None, + rid_version: RIDVersion | None = None, ): """ Test that a _evaluate function is handeling a specifc value as invalid when observed. @@ -759,7 +760,7 @@ def _assert_generic_evaluator_invalid_observed_value( ) -def _assert_generic_evaluator_defaults( +def _assert_generic_evaluator_defaults[T2, T]( *fct_and_setters: list[Any], default_value: T2, valid_value: T ): """ @@ -844,7 +845,7 @@ def _assert_generic_evaluator_dont_have_default( def _assert_generic_evaluator_equivalent( - *fct_and_setters: list[Any], v1: T, v2: T, rid_version: Optional[RIDVersion] = None + *fct_and_setters: list[Any], v1: T, v2: T, rid_version: RIDVersion | None = None ): """ Test that a _evaluate function is considering two value as equivalent. @@ -867,7 +868,7 @@ def _assert_generic_evaluator_equivalent( def _assert_generic_evaluator_not_equivalent( - *fct_and_setters: list[Any], v1: T, v2: T, rid_version: Optional[RIDVersion] = None + *fct_and_setters: list[Any], v1: T, v2: T, rid_version: RIDVersion | None = None ): """ Test that a _evaluate function is considering two value as not equivalent. diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator.py b/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator.py index e882baaa15..05cdd02438 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator.py @@ -1,7 +1,6 @@ import datetime import math from dataclasses import dataclass, field -from typing import Dict, List, Optional, Set, Tuple, Union import arrow import s2sphere @@ -57,7 +56,7 @@ @dataclass -class DPObservedFlight(object): +class DPObservedFlight: query: FetchedUSSFlights flight_index: int @@ -66,7 +65,7 @@ def id(self) -> str: return self.query.flights[self.flight_index].id @property - def most_recent_position(self) -> Optional[Position]: + def most_recent_position(self) -> Position | None: return self.query.flights[self.flight_index].most_recent_position @property @@ -76,7 +75,7 @@ def flight(self) -> fetch.rid.Flight: # TODO we may rather want to expose the whole flight object (self.query.flights[self.flight]) # and let callers access subfields directly (will be handled in separate PR) @property - def timestamp_accuracy(self) -> Optional[float]: + def timestamp_accuracy(self) -> float | None: return self.query.flights[self.flight_index].timestamp_accuracy @property @@ -88,18 +87,18 @@ def flights_url(self) -> str: return self.query.flights_url -ObservationType = Union[Flight, DPObservedFlight] +ObservationType = Flight | DPObservedFlight @dataclass -class TelemetryMapping(object): +class TelemetryMapping: injected_flight: InjectedFlight telemetry_index: int observed_flight: ObservationType @dataclass -class FetchedToInjectedCache(object): +class FetchedToInjectedCache: mappings: dict[str, ParticipantID] = field(default_factory=dict) """Mapping is a map of URL -> ParticipantID that we found until now""" @@ -108,9 +107,9 @@ class FetchedToInjectedCache(object): def map_observations_to_injected_flights( - injected_flights: List[InjectedFlight], - observed_flights: List[ObservationType], -) -> Dict[str, TelemetryMapping]: + injected_flights: list[InjectedFlight], + observed_flights: list[ObservationType], +) -> dict[str, TelemetryMapping]: """Identify which of the observed flights (if any) matches to each of the injected flights This function assumes there is no valid situation in which a particular observed flight could be one of multiple @@ -123,7 +122,7 @@ def map_observations_to_injected_flights( Returns: Mapping between InjectedFlight and observed Flight, indexed by injection_id. """ - mapping: Dict[str, TelemetryMapping] = {} + mapping: dict[str, TelemetryMapping] = {} for injected_flight in injected_flights: smallest_distance = 1e9 best_match = None @@ -175,10 +174,10 @@ def map_observations_to_injected_flights( def map_fetched_to_injected_flights( - injected_flights: List[InjectedFlight], - fetched_flights: List[FetchedUSSFlights], + injected_flights: list[InjectedFlight], + fetched_flights: list[FetchedUSSFlights], query_cache: FetchedToInjectedCache, -) -> Dict[str, TelemetryMapping]: +) -> dict[str, TelemetryMapping]: """Identify which of the fetched flights (if any) matches to each of the injected flights. See `map_observations_to_injected_flights`. @@ -225,7 +224,7 @@ def map_fetched_to_injected_flights( return tel_mapping -class RIDObservationEvaluator(object): +class RIDObservationEvaluator: """Evaluates observations of an RID system over time. This evaluator observes a set of provided RIDSystemObservers in @@ -238,10 +237,10 @@ class RIDObservationEvaluator(object): def __init__( self, test_scenario: TestScenario, - injected_flights: List[InjectedFlight], + injected_flights: list[InjectedFlight], config: EvaluationConfiguration, rid_version: RIDVersion, - dss: Optional[DSSInstance] = None, + dss: DSSInstance | None = None, ): self._test_scenario = test_scenario self._common_dictionary_evaluator = RIDCommonDictionaryEvaluator( @@ -263,13 +262,13 @@ def __init__( raise ValueError( f"Cannot evaluate a system using RID version {rid_version} with a DSS using RID version {dss.rid_version}" ) - self._retrieved_flight_details: Set[str] = ( + self._retrieved_flight_details: set[str] = ( set() ) # Contains the observed IDs of the flights whose details were retrieved. def evaluate_system_instantaneously( self, - observers: List[RIDSystemObserver], + observers: list[RIDSystemObserver], rect: s2sphere.LatLngRect, ) -> None: if self._dss: @@ -330,9 +329,9 @@ def _evaluate_observation( self, observer: RIDSystemObserver, rect: s2sphere.LatLngRect, - observation: Optional[GetDisplayDataResponse], + observation: GetDisplayDataResponse | None, query: fetch.Query, - verified_sps: Set[str], + verified_sps: set[str], ) -> None: diagonal_km = ( rect.lo().get_distance(rect.hi()).degrees * geo.EARTH_CIRCUMFERENCE_KM / 360 @@ -371,7 +370,7 @@ def _evaluate_normal_observation( rect: s2sphere.LatLngRect, observation: GetDisplayDataResponse, query: fetch.Query, - verified_sps: Set[str], + verified_sps: set[str], ) -> None: # Make sure we didn't get duplicate flight IDs flights_by_id = {} @@ -516,7 +515,7 @@ def _classify_clustered_presence( self, rect: s2sphere.LatLngRect, query: fetch.Query, - ) -> Tuple[List[InjectedFlight], List[InjectedFlight]]: + ) -> tuple[list[InjectedFlight], list[InjectedFlight]]: present = [] uncertain = [] @@ -640,7 +639,7 @@ def _evaluate_clusters_observation( def _evaluate_clusters_obfuscation_distance( self, observer: RIDSystemObserver, - clusters: List[Cluster], + clusters: list[Cluster], ) -> None: # TODO: Improve this check by using the positions of the flights to compute the minimum obfuscation distance. # For this we need the assignment of flights per cluster. Currently this checks only if the cluster has @@ -649,7 +648,7 @@ def _evaluate_clusters_obfuscation_distance( # Alternatively, or as a first step, some better checks could be performed by checking for situations that are # wrong for sure. (see https://github.com/interuss/monitoring/pull/121#discussion_r1265972147) - cluster_rects: List[LatLngRect] = [ + cluster_rects: list[LatLngRect] = [ LatLngRect.from_point_pair( LatLng.from_degrees(c.corners[0].lat, c.corners[0].lng), LatLng.from_degrees(c.corners[1].lat, c.corners[1].lng), @@ -680,11 +679,11 @@ def _evaluate_clusters_obfuscation_distance( def _evaluate_obfuscated_clusters_observation( self, observer: RIDSystemObserver, - obfuscated_clusters: List[Cluster], - expected_flights: List[InjectedFlight], - uncertain_flights: List[InjectedFlight], + obfuscated_clusters: list[Cluster], + expected_flights: list[InjectedFlight], + uncertain_flights: list[InjectedFlight], ) -> None: - cluster_rects: List[LatLngRect] = [ + cluster_rects: list[LatLngRect] = [ LatLngRect.from_point_pair( LatLng.from_degrees(c.corners[0].lat, c.corners[0].lng), LatLng.from_degrees(c.corners[1].lat, c.corners[1].lng), @@ -740,7 +739,7 @@ def _evaluate_sp_observation( self, requested_area: s2sphere.LatLngRect, sp_observation: FetchedFlights, - mappings: Dict[str, TelemetryMapping], + mappings: dict[str, TelemetryMapping], ) -> None: # Note: This step currently uses the DSS endpoint to perform a one-time query for ISAs, but this # endpoint is not strictly required. The PUT Subscription endpoint, followed immediately by the @@ -779,7 +778,7 @@ def _evaluate_normal_sp_observation( self, requested_area: s2sphere.LatLngRect, sp_observation: FetchedFlights, - mappings: Dict[str, TelemetryMapping], + mappings: dict[str, TelemetryMapping], ) -> None: _evaluate_flight_presence( "uss_qualifier, acting as Display Provider", @@ -910,7 +909,7 @@ def _evaluate_normal_sp_observation( def _evaluate_area_too_large_sp_observation( self, - mappings: Dict[str, TelemetryMapping], + mappings: dict[str, TelemetryMapping], rect: s2sphere.LatLngRect, diagonal: float, ) -> None: @@ -994,7 +993,7 @@ def fail_check(): fail_check() -class DisconnectedUASObservationEvaluator(object): +class DisconnectedUASObservationEvaluator: """Evaluates observations of an RID system over time. This evaluator observes a set of provided RIDSystemObservers in @@ -1007,7 +1006,7 @@ class DisconnectedUASObservationEvaluator(object): def __init__( self, test_scenario: TestScenario, - injected_flights: List[InjectedFlight], + injected_flights: list[InjectedFlight], config: EvaluationConfiguration, rid_version: RIDVersion, dss: DSSInstance, @@ -1025,15 +1024,15 @@ def __init__( raise ValueError( f"Cannot evaluate a system using RID version {rid_version} with a DSS using RID version {dss.rid_version}" ) - self._retrieved_flight_details: Set[str] = ( + self._retrieved_flight_details: set[str] = ( set() ) # Contains the observed IDs of the flights whose details were retrieved. # Keep track of the flights that we have observed as having been 'disconnected' # (last observed telemetry corresponds to last injected one within the window where data is returned) - self._observed_disconnections: Set[str] = set() + self._observed_disconnections: set[str] = set() - def remaining_disconnections_to_observe(self) -> Dict[str, str]: + def remaining_disconnections_to_observe(self) -> dict[str, str]: return { f.flight.injection_id: f.uss_participant_id for f in self._injected_flights @@ -1085,7 +1084,7 @@ def evaluate_disconnected_flights( def _evaluate_sp_observation( self, sp_observation: FetchedFlights, - mappings: Dict[str, TelemetryMapping], + mappings: dict[str, TelemetryMapping], ) -> None: # Note: This step currently uses the DSS endpoint to perform a one-time query for ISAs, but this # endpoint is not strictly required. The PUT Subscription endpoint, followed immediately by the @@ -1111,7 +1110,7 @@ def _evaluate_sp_observation( def _evaluate_sp_observation_of_disconnected_flights( self, sp_observation: FetchedFlights, - mappings: Dict[str, TelemetryMapping], + mappings: dict[str, TelemetryMapping], ) -> None: # TODO we will want to reuse the code in RIDObservationEvaluator rather than copy-pasting it _evaluate_flight_presence( @@ -1186,7 +1185,7 @@ def _evaluate_sp_observation_of_disconnected_flights( ) -class NotificationsEvaluator(object): +class NotificationsEvaluator: """Evaluates observations of an RID system over time. This evaluator observes a set of provided RIDSystemObservers in @@ -1200,7 +1199,7 @@ def __init__( service_providers: NetRIDServiceProviders, config: EvaluationConfiguration, rid_version: RIDVersion, - initial_notifications: dict[str, List[str]], + initial_notifications: dict[str, list[str]], notification_before: datetime.datetime, notification_after: datetime.datetime, ): @@ -1257,7 +1256,7 @@ def _retrieve_notifications(self): ) -def _chronological_positions(f: Flight) -> List[s2sphere.LatLng]: +def _chronological_positions(f: Flight) -> list[s2sphere.LatLng]: """ Returns the recent positions of the flight, ordered by time with the oldest first, and the most recent last. """ @@ -1267,7 +1266,7 @@ def _chronological_positions(f: Flight) -> List[s2sphere.LatLng]: ] -def _sliding_triples(points: List[s2sphere.LatLng]) -> List[List[s2sphere.LatLng]]: +def _sliding_triples(points: list[s2sphere.LatLng]) -> list[list[s2sphere.LatLng]]: """ Returns a list of triples of consecutive positions in passed the list. """ @@ -1276,12 +1275,12 @@ def _sliding_triples(points: List[s2sphere.LatLng]) -> List[List[s2sphere.LatLng def _evaluate_flight_presence( observer_participant_id: str, - observation_queries: List[Query], + observation_queries: list[Query], observer_participant_is_relevant: bool, - mapping_by_injection_id: Dict[str, TelemetryMapping], - verified_sps: Set[str], + mapping_by_injection_id: dict[str, TelemetryMapping], + verified_sps: set[str], test_scenario: TestScenario, - injected_flights: List[InjectedFlight], + injected_flights: list[InjectedFlight], rid_version: RIDVersion, evaluation_config: EvaluationConfiguration, ): diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator_test.py b/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator_test.py index dafbff3ba4..2e92a25436 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator_test.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/display_data_evaluator_test.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime import s2sphere @@ -34,7 +34,7 @@ def step_under_test(self: UnitTestScenario): def test_evaluate_sp_flight_recent_positions(): - some_time = datetime.now(timezone.utc) + some_time = datetime.now(UTC) # All samples within last minute: should pass _assert_evaluate_sp_flight_recent_positions( mock_flight(some_time, 7, 10), some_time, True @@ -74,7 +74,7 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(0.5, 0.5), ), - mock_flight(datetime.now(timezone.utc), 0, 10), + mock_flight(datetime.now(UTC), 0, 10), True, ) # Mock flight with one recent position: should pass event if outside of area @@ -83,7 +83,7 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(0.5, 0.5), ), - mock_flight(datetime.now(timezone.utc), 1, 10), + mock_flight(datetime.now(UTC), 1, 10), True, ) @@ -93,7 +93,7 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(2, 2), ), - mock_flight(datetime.now(timezone.utc), 2, 10), + mock_flight(datetime.now(UTC), 2, 10), True, ) @@ -103,14 +103,14 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(0.5, 0.5), ), - mock_flight(datetime.now(timezone.utc), 2, 10), + mock_flight(datetime.now(UTC), 2, 10), False, ) # Mock flight with two recent positions, one of which is outside area: should pass - f2_1 = mock_flight(datetime.now(timezone.utc), 0, 0) + f2_1 = mock_flight(datetime.now(UTC), 0, 0) f2_1.v22a_value.recent_positions = to_positions( - [(1.0, 1.0), (-1.0, -1.0)], datetime.now(timezone.utc) + [(1.0, 1.0), (-1.0, -1.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -121,9 +121,9 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): True, ) - f2_2 = mock_flight(datetime.now(timezone.utc), 0, 0) + f2_2 = mock_flight(datetime.now(UTC), 0, 0) f2_2.v22a_value.recent_positions = to_positions( - [(-1.0, -1.0), (1.0, 1.0)], datetime.now(timezone.utc) + [(-1.0, -1.0), (1.0, 1.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -140,14 +140,14 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(0.5, 0.5), ), - mock_flight(datetime.now(timezone.utc), 3, 10), + mock_flight(datetime.now(UTC), 3, 10), False, ) # Mock flight with 3 recent positions, the second of which is in the area: should pass - f3_1 = mock_flight(datetime.now(timezone.utc), 0, 0) + f3_1 = mock_flight(datetime.now(UTC), 0, 0) f3_1.v22a_value.recent_positions = to_positions( - [(-1.0, -1.0), (1.0, 1.0), (3.0, 3.0)], datetime.now(timezone.utc) + [(-1.0, -1.0), (1.0, 1.0), (3.0, 3.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -159,9 +159,9 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): ) # Mock flight with 3 recent positions, only the last of which is in the area: should fail - f3_2 = mock_flight(datetime.now(timezone.utc), 0, 0) + f3_2 = mock_flight(datetime.now(UTC), 0, 0) f3_2.v22a_value.recent_positions = to_positions( - [(-1.0, -1.0), (3.0, 3.0), (1.0, 1.0)], datetime.now(timezone.utc) + [(-1.0, -1.0), (3.0, 3.0), (1.0, 1.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -173,9 +173,9 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): ) # Mock flight with 3 recent positions, only the first of which is in the area: should fail - f3_3 = mock_flight(datetime.now(timezone.utc), 0, 0) + f3_3 = mock_flight(datetime.now(UTC), 0, 0) f3_3.v22a_value.recent_positions = to_positions( - [(1.0, 1.0), (3.0, 3.0), (-1.0, -1.0)], datetime.now(timezone.utc) + [(1.0, 1.0), (3.0, 3.0), (-1.0, -1.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -192,14 +192,14 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(2, 2), ), - mock_flight(datetime.now(timezone.utc), 3, 10), + mock_flight(datetime.now(UTC), 3, 10), True, ) # Mock flight with 4 recent positions, last position outside requested area: should pass - f4_1 = mock_flight(datetime.now(timezone.utc), 0, 0) + f4_1 = mock_flight(datetime.now(UTC), 0, 0) f4_1.v22a_value.recent_positions = to_positions( - [(1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (3.0, 3.0)], datetime.now(timezone.utc) + [(1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (3.0, 3.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -211,9 +211,9 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): ) # Mock flight with 4 recent positions, first position outside requested area: should pass - f4_2 = mock_flight(datetime.now(timezone.utc), 0, 0) + f4_2 = mock_flight(datetime.now(UTC), 0, 0) f4_2.v22a_value.recent_positions = to_positions( - [(3.0, 3.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0)], datetime.now(timezone.utc) + [(3.0, 3.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0)], datetime.now(UTC) ) _assert_evaluate_sp_flight_recent_positions_crossing_area_boundary( s2sphere.LatLngRect( @@ -230,7 +230,7 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(2, 2), ), - mock_flight(datetime.now(timezone.utc), 7, 10), + mock_flight(datetime.now(UTC), 7, 10), True, ) @@ -240,6 +240,6 @@ def test_evaluate_sp_flight_recent_positions_crossing_area_boundary(): s2sphere.LatLng.from_degrees(0.0, 0.0), s2sphere.LatLng.from_degrees(0.5, 0.5), ), - mock_flight(datetime.now(timezone.utc), 7, 10), + mock_flight(datetime.now(UTC), 7, 10), False, ) diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/dss_wrapper.py b/monitoring/uss_qualifier/scenarios/astm/netrid/dss_wrapper.py index 9eb689d4da..64ad2f0d22 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/dss_wrapper.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/dss_wrapper.py @@ -1,5 +1,5 @@ import datetime -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any import s2sphere from implicitdict import StringBasedDateTime @@ -31,7 +31,7 @@ MAX_SKEW = 1e-6 # seconds maximum difference between expected and actual timestamps -class DSSWrapper(object): +class DSSWrapper: """Wraps a DSS instance with test checks.""" # TODO: adapt other functions with corresponding test step and sub-checks like it is done for put_isa @@ -74,8 +74,8 @@ def handle_query_result( check: PendingCheck, q: RIDQuery, fail_msg: str, - required_status_code: Optional[Set[int]] = None, - fail_details: Optional[str] = None, + required_status_code: set[int] | None = None, + fail_details: str | None = None, ): """ Handle the result of the query, based on the expected result codes versus the actual one, @@ -113,9 +113,9 @@ def handle_query_result( def search_isas( self, main_check: PendingCheck, - area: List[s2sphere.LatLng], - start_time: Optional[datetime.datetime] = None, - end_time: Optional[datetime.datetime] = None, + area: list[s2sphere.LatLng], + start_time: datetime.datetime | None = None, + end_time: datetime.datetime | None = None, ) -> FetchedISAs: """Search for ISAs at the DSS. @@ -162,10 +162,10 @@ def search_isas( def search_isas_expect_response_code( self, main_check: PendingCheck, - expected_error_codes: Set[int], - area: List[s2sphere.LatLng], - start_time: Optional[datetime.datetime] = None, - end_time: Optional[datetime.datetime] = None, + expected_error_codes: set[int], + area: list[s2sphere.LatLng], + start_time: datetime.datetime | None = None, + end_time: datetime.datetime | None = None, ) -> FetchedISAs: """Attempt to search for ISAs at the DSS, and expect the specified HTTP response code. @@ -233,7 +233,7 @@ def get_isa( def get_isa_expect_response_code( self, check: PendingCheck, - expected_error_codes: Set[int], + expected_error_codes: set[int], isa_id: str, ) -> FetchedISA: """Attempt to fetch an ISA at the DSS, and expect the specified HTTP response code. @@ -263,16 +263,16 @@ def get_isa_expect_response_code( def put_isa_expect_response_code( self, check: PendingCheck, - expected_error_codes: Set[int], - area_vertices: List[s2sphere.LatLng], + expected_error_codes: set[int], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, start_time: datetime.datetime, end_time: datetime.datetime, uss_base_url: str, isa_id: str, - isa_version: Optional[str] = None, - do_not_notify: Optional[Union[str, List[str]]] = None, + isa_version: str | None = None, + do_not_notify: str | list[str] | None = None, ) -> ISAChange: mutated_isa = mutate.put_isa( area_vertices=area_vertices, @@ -304,15 +304,15 @@ def put_isa_expect_response_code( def put_isa( self, main_check: PendingCheck, - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, start_time: datetime.datetime, end_time: datetime.datetime, uss_base_url: str, isa_id: str, - isa_version: Optional[str] = None, - do_not_notify: Optional[Union[str, List[str]]] = None, + isa_version: str | None = None, + do_not_notify: str | list[str] | None = None, ) -> ISAChange: """Create or update an ISA at the DSS. @@ -381,8 +381,8 @@ def del_isa( main_check: PendingCheck, isa_id: str, isa_version: str, - do_not_notify: Optional[Union[str, List[str]]] = None, - expected_isa_params: Optional[Dict[str, Any]] = None, + do_not_notify: str | list[str] | None = None, + expected_isa_params: dict[str, Any] | None = None, ) -> ISAChange: """Delete an ISA at the DSS. @@ -475,10 +475,10 @@ def _fail_sub_check( def del_isa_expect_response_code( self, main_check: PendingCheck, - expected_error_codes: Set[int], + expected_error_codes: set[int], isa_id: str, isa_version: str, - do_not_notify: Optional[Union[str, List[str]]] = None, + do_not_notify: str | list[str] | None = None, ) -> ISAChange: """Attempt to delete an ISA at the DSS, and expect the specified HTTP response code. @@ -513,7 +513,7 @@ def cleanup_isa( self, check: PendingCheck, isa_id: str, - ) -> Optional[ISAChange]: + ) -> ISAChange | None: """Cleanup an ISA at the DSS. Does not fail if the ISA is not found. A check fail is considered of medium severity and won't raise error. @@ -557,8 +557,8 @@ def cleanup_isa( def search_subs_expect_response_code( self, check: PendingCheck, - expected_codes: Set[int], - area: List[s2sphere.LatLng], + expected_codes: set[int], + area: list[s2sphere.LatLng], ) -> FetchedSubscriptions: """Search for subscriptions at the DSS, expecting one of the passed HTTP response codes. @@ -589,7 +589,7 @@ def search_subs_expect_response_code( def search_subs( self, check: PendingCheck, - area: List[s2sphere.LatLng], + area: list[s2sphere.LatLng], ) -> FetchedSubscriptions: """Search for subscriptions at the DSS. A check fail is considered of high severity and as such will raise a ScenarioCannotContinueError. @@ -619,7 +619,7 @@ def search_subs( def get_sub_expect_response_code( self, check: PendingCheck, - expected_response_codes: Set[int], + expected_response_codes: set[int], sub_id: str, ) -> FetchedSubscription: """Get a subscription at the DSS, expecting one the passed HTTP response codes. @@ -719,15 +719,15 @@ def no_sub( def put_sub_expect_response_code( self, check: PendingCheck, - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], - expected_error_codes: Set[int], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, + expected_error_codes: set[int], uss_base_url: str, sub_id: str, - sub_version: Optional[str] = None, + sub_version: str | None = None, ) -> ChangedSubscription: """Attempt to create or update a subscription at the DSS, and expect the specified HTTP response code. @@ -767,14 +767,14 @@ def put_sub_expect_response_code( def put_sub( self, check: PendingCheck, - area_vertices: List[s2sphere.LatLng], + area_vertices: list[s2sphere.LatLng], alt_lo: float, alt_hi: float, - start_time: Optional[datetime.datetime], - end_time: Optional[datetime.datetime], + start_time: datetime.datetime | None, + end_time: datetime.datetime | None, uss_base_url: str, sub_id: str, - sub_version: Optional[str] = None, + sub_version: str | None = None, ) -> ChangedSubscription: """Create or update a subscription at the DSS. A check fail is considered of high severity and as such will raise a ScenarioCannotContinueError. @@ -811,7 +811,7 @@ def put_sub( def del_sub_expect_response_code( self, check: PendingCheck, - expected_response_codes: Set[int], + expected_response_codes: set[int], sub_id: str, sub_version: str, ) -> ChangedSubscription: @@ -886,7 +886,7 @@ def del_sub( def cleanup_subs_in_area( self, - area: List[s2sphere.LatLng], + area: list[s2sphere.LatLng], ): """Cleanup any subscription that is returned for the search in the provided area""" @@ -923,7 +923,7 @@ def cleanup_subs_in_area( def cleanup_sub( self, sub_id: str, - ) -> Optional[ChangedSubscription]: + ) -> ChangedSubscription | None: """Cleanup a subscription at the DSS. Does not fail if it is not found. A check fail is considered of medium severity and won't raise error. @@ -978,8 +978,8 @@ def raw_request_with_expected_code( check: PendingCheck, method: str, url_path: str, - json: Dict[str, Any], - expected_error_codes: Set[int], + json: dict[str, Any], + expected_error_codes: set[int], fail_msg: str, ) -> RIDQuery: """For passing raw requests to the underlying client. diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/injected_flight_collection.py b/monitoring/uss_qualifier/scenarios/astm/netrid/injected_flight_collection.py index c20189a9f9..7c8f2a5998 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/injected_flight_collection.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/injected_flight_collection.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List import arrow from s2sphere import LatLng, LatLngRect @@ -8,10 +7,10 @@ from monitoring.uss_qualifier.scenarios.astm.netrid.injection import InjectedFlight -class InjectedFlightCollection(object): - _injected_flights: List[InjectedFlight] +class InjectedFlightCollection: + _injected_flights: list[InjectedFlight] - def __init__(self, injected_flights: List[InjectedFlight]): + def __init__(self, injected_flights: list[InjectedFlight]): self._injected_flights = injected_flights def get_query_rect( diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/injection.py b/monitoring/uss_qualifier/scenarios/astm/netrid/injection.py index 275bfdf49b..7d2532c13c 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/injection.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/injection.py @@ -1,6 +1,5 @@ import uuid from datetime import datetime -from typing import List, Tuple import arrow from implicitdict import ImplicitDict @@ -36,13 +35,13 @@ def inject_flights( test_scenario: TestScenario, flights_data_res: FlightDataResource, service_providers_res: NetRIDServiceProviders, -) -> Tuple[List[InjectedFlight], List[InjectedTest]]: +) -> tuple[list[InjectedFlight], list[InjectedTest]]: test_id = str(uuid.uuid4()) test_flights = flights_data_res.get_test_flights() service_providers = service_providers_res.service_providers - injected_flights: List[InjectedFlight] = [] - injected_tests: List[InjectedTest] = [] + injected_flights: list[InjectedFlight] = [] + injected_tests: list[InjectedTest] = [] if len(service_providers) > len(test_flights): raise ValueError( @@ -134,7 +133,7 @@ def inject_flights( return injected_flights, injected_tests -def injected_flights_errors(injected_flights: List[InjectedFlight]) -> List[str]: +def injected_flights_errors(injected_flights: list[InjectedFlight]) -> list[str]: """Determine whether each telemetry in each injected flight can be easily distinguished from each other. Args: @@ -142,7 +141,7 @@ def injected_flights_errors(injected_flights: List[InjectedFlight]) -> List[str] Returns: List of error messages, or an empty list if no errors. """ - errors: List[str] = [] + errors: list[str] = [] for f1, injected_flight in enumerate(injected_flights): for t1, injected_telemetry in enumerate(injected_flight.flight.telemetry): for t2, other_telemetry in enumerate( @@ -170,7 +169,7 @@ def get_user_notifications( service_providers_res: NetRIDServiceProviders, after: datetime, before: datetime, -) -> dict[str, List[str]]: +) -> dict[str, list[str]]: service_providers = service_providers_res.service_providers notifications = {} diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/store_flight_data.py b/monitoring/uss_qualifier/scenarios/astm/netrid/store_flight_data.py index a4ae243724..3697a65f03 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/store_flight_data.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/store_flight_data.py @@ -87,9 +87,9 @@ def run(self, context: ExecutionContext): "geometry": shapely.geometry.mapping(line), } feature_collection["features"].append(line_feature) - path_file_name = "track_%s.geojson" % str( - track_id + 1 - ) # Avoid Zero based numbering + path_file_name = ( + f"track_{str(track_id + 1)}.geojson" # Avoid Zero based numbering + ) tracks_file_path = os.path.join( self._storage_config.storage_configuration.geojson_tracks_path, diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/v19/aggregate_checks.py b/monitoring/uss_qualifier/scenarios/astm/netrid/v19/aggregate_checks.py index bb3a325fc0..f792e5d06f 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/v19/aggregate_checks.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/v19/aggregate_checks.py @@ -1,5 +1,3 @@ -from typing import Optional - from monitoring.monitorlib.rid import RIDVersion from monitoring.uss_qualifier.resources.astm.f3411 import DSSInstancesResource from monitoring.uss_qualifier.resources.dev import TestExclusionsResource @@ -19,7 +17,7 @@ def __init__( service_providers: NetRIDServiceProviders, observers: NetRIDObserversResource, dss_instances: DSSInstancesResource, - test_exclusions: Optional[TestExclusionsResource] = None, + test_exclusions: TestExclusionsResource | None = None, ): super().__init__(service_providers, observers, dss_instances, test_exclusions) self._rid_version = RIDVersion.f3411_19 diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/v22a/aggregate_checks.py b/monitoring/uss_qualifier/scenarios/astm/netrid/v22a/aggregate_checks.py index bd32930463..140be819e4 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/v22a/aggregate_checks.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/v22a/aggregate_checks.py @@ -1,5 +1,3 @@ -from typing import Optional - from monitoring.monitorlib.rid import RIDVersion from monitoring.uss_qualifier.resources.astm.f3411 import DSSInstancesResource from monitoring.uss_qualifier.resources.dev import TestExclusionsResource @@ -19,7 +17,7 @@ def __init__( service_providers: NetRIDServiceProviders, observers: NetRIDObserversResource, dss_instances: DSSInstancesResource, - test_exclusions: Optional[TestExclusionsResource] = None, + test_exclusions: TestExclusionsResource | None = None, ): super().__init__(service_providers, observers, dss_instances, test_exclusions) self._rid_version = RIDVersion.f3411_22a diff --git a/monitoring/uss_qualifier/scenarios/astm/netrid/virtual_observer.py b/monitoring/uss_qualifier/scenarios/astm/netrid/virtual_observer.py index e23a85f16f..79c4fe0799 100644 --- a/monitoring/uss_qualifier/scenarios/astm/netrid/virtual_observer.py +++ b/monitoring/uss_qualifier/scenarios/astm/netrid/virtual_observer.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from datetime import datetime, timedelta -from typing import Callable, List, Optional import arrow from loguru import logger @@ -11,7 +11,7 @@ ) -class VirtualObserver(object): +class VirtualObserver: """Defines the behavior of a virtual human-like observer. The observer wants to look at the specified collection of flights, and this @@ -34,7 +34,7 @@ class indicates their behavior by computing the query rectangle at each _repeat_query_counter: int = 0 """Number of repeated queries to the same rectangle; related to _repeat_query_rect_period""" - _last_rect: Optional[LatLngRect] = None + _last_rect: LatLngRect | None = None """The most recent query rectangle""" def __init__( @@ -75,7 +75,7 @@ def get_last_time_of_interest(self) -> datetime: def start_polling( self, interval: timedelta, - diagonals_m: List[float], + diagonals_m: list[float], poll_fct: Callable[[LatLngRect], bool], ) -> None: """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/aggregate_checks.py b/monitoring/uss_qualifier/scenarios/astm/utm/aggregate_checks.py index dc33dbda84..d7c8957649 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/aggregate_checks.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/aggregate_checks.py @@ -1,5 +1,3 @@ -from typing import Dict, List - from uas_standards.astm.f3548.v21 import constants from monitoring.monitorlib import fetch @@ -11,8 +9,8 @@ class AggregateChecks(TestScenario): - _queries: List[fetch.Query] - _attributed_queries: Dict[ParticipantID, Dict[QueryType, List[fetch.Query]]] = {} + _queries: list[fetch.Query] + _attributed_queries: dict[ParticipantID, dict[QueryType, list[fetch.Query]]] = {} def __init__( self, @@ -135,12 +133,12 @@ def _confirm_test_harness_queries_work(self): def _validate_participant_test_interop_instance( self, participant_id: str, - participant_queries: dict[QueryType, List[fetch.Query]], + participant_queries: dict[QueryType, list[fetch.Query]], ): # Keep track of how many interactions we've found for this participant # if there is None the condition is not met test_interactions = 0 - success_by_type: Dict[QueryType, bool] = {} + success_by_type: dict[QueryType, bool] = {} for query_type, queries in participant_queries.items(): if _is_interop_test_interaction(query_type): test_interactions += len(queries) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/clear_area_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/clear_area_validation.py index e738c2c1d1..5854afc3a7 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/clear_area_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/clear_area_validation.py @@ -1,5 +1,3 @@ -from typing import List - from uas_standards.astm.f3548.v21.api import OperationalIntentReference from monitoring.monitorlib.fetch import QueryError @@ -11,9 +9,9 @@ def validate_clear_area( scenario: TestScenario, dss: DSSInstance, - areas: List[Volume4D], + areas: list[Volume4D], ignore_self: bool, -) -> List[OperationalIntentReference]: +) -> list[OperationalIntentReference]: found_intents = [] for area in areas: with scenario.check("DSS responses", [dss.participant_id]) as check: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/get_op_data_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/get_op_data_validation.py index 81e65a6568..34f6de05af 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/get_op_data_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/get_op_data_validation.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional, Set - import arrow from uas_standards.astm.f3548.v21.api import EntityID from uas_standards.astm.f3548.v21.constants import Scope @@ -64,7 +62,7 @@ class GetOpResponseDataValidationByUSS(TestScenario): flight_1: FlightInfoTemplate flight_2: FlightInfoTemplate - op_intent_ids: Set[EntityID] + op_intent_ids: set[EntityID] tested_uss_client: FlightPlannerClient mock_uss: MockUSSClient @@ -76,7 +74,7 @@ def __init__( tested_uss: FlightPlannerResource, mock_uss: MockUSSResource, dss: DSSInstanceResource, - flight_intents: Optional[FlightIntentsResource] = None, + flight_intents: FlightIntentsResource | None = None, ): super().__init__() self.tested_uss_client = tested_uss.client @@ -151,7 +149,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() - def _plan_successfully_test_case(self, times: Dict[TimeDuringTest, Time]): + def _plan_successfully_test_case(self, times: dict[TimeDuringTest, Time]): times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime) flight_2 = self.flight_2.resolve(times) @@ -226,7 +224,7 @@ def _plan_successfully_test_case(self, times: Dict[TimeDuringTest, Time]): delete_flight(self, self.mock_uss_client, self.flight_2_id) self.end_test_step() - def _plan_unsuccessfully_test_case(self, times: Dict[TimeDuringTest, Time]): + def _plan_unsuccessfully_test_case(self, times: dict[TimeDuringTest, Time]): times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime) flight_info = self.flight_2.resolve(times) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/expected_interactions_test_steps.py b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/expected_interactions_test_steps.py index 537f0c43ae..a44317b541 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/expected_interactions_test_steps.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/expected_interactions_test_steps.py @@ -1,7 +1,6 @@ from __future__ import annotations from datetime import datetime -from typing import Set from implicitdict import ImplicitDict, StringBasedDateTime from uas_standards.astm.f3548.v21.api import ( @@ -67,7 +66,7 @@ def expect_no_interuss_post_interactions( scenario: TestScenarioType, mock_uss: MockUSSClient, st: StringBasedDateTime, - shared_op_intent_ids: Set[EntityID], + shared_op_intent_ids: set[EntityID], participant_id: str, ): """This step checks no notification about an unexpected operational intent is sent to any USS within the required time window (as no DSS entity was created). diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/wait.py b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/wait.py index b0f5a60c86..5a71a966cb 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/wait.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/data_exchange_validation/test_steps/wait.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from datetime import timedelta -from typing import Callable, List, Tuple import arrow @@ -18,7 +18,7 @@ """Time interval to wait between two calls to get interactions from Mock USS""" -def wait_in_intervals(func) -> Callable[..., Tuple[List[Interaction], Query]]: +def wait_in_intervals(func) -> Callable[..., tuple[list[Interaction], Query]]: """ This wrapper calls the given function in intervals till desired interactions (of notifications) are returned, or till the max wait time is reached. @@ -27,7 +27,7 @@ def wait_in_intervals(func) -> Callable[..., Tuple[List[Interaction], Query]]: """ - def wrapper(*args, **kwargs) -> Tuple[List[Interaction], Query]: + def wrapper(*args, **kwargs) -> tuple[list[Interaction], Query]: wait_until = arrow.utcnow().datetime + timedelta( seconds=MaxTimeToWaitForSubscriptionNotificationSeconds ) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py index f5032962d0..4abfa3b8f4 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/authentication_validation.py @@ -1,5 +1,3 @@ -from typing import Optional - from uas_standards.astm.f3548.v21.api import UssAvailabilityState from uas_standards.astm.f3548.v21.constants import Scope @@ -54,9 +52,9 @@ class AuthenticationValidation(TestScenario): _test_id: str """Base identifier for the entities that will be created""" - _scd_dss: Optional[DSSInstance] = None - _availability_dss: Optional[DSSInstance] = None - _constraints_dss: Optional[DSSInstance] = None + _scd_dss: DSSInstance | None = None + _availability_dss: DSSInstance | None = None + _constraints_dss: DSSInstance | None = None def __init__( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/availability_api_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/availability_api_validator.py index fb0028f483..fe1cf6b382 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/availability_api_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/availability_api_validator.py @@ -1,5 +1,3 @@ -from typing import Optional - from uas_standards.astm.f3548.v21.api import ( OPERATIONS, OperationID, @@ -19,7 +17,7 @@ class AvailabilityAuthValidator: - _current_availability: Optional[UssAvailabilityStatusResponse] = None + _current_availability: UssAvailabilityStatusResponse | None = None def __init__( self, @@ -29,7 +27,7 @@ def __init__( test_id: str, no_auth_session: UTMClientSession, invalid_token_session: UTMClientSession, - test_wrong_scope: Optional[str] = None, + test_wrong_scope: str | None = None, test_missing_scope: bool = False, ): """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/cr_api_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/cr_api_validator.py index e82513f9f4..6876dccc65 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/cr_api_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/cr_api_validator.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Optional from implicitdict import ImplicitDict, StringBasedDateTime from uas_standards.astm.f3548.v21.api import ( @@ -37,7 +36,7 @@ def __init__( planning_area_volume4d: Volume4D, no_auth_session: UTMClientSession, invalid_token_session: UTMClientSession, - test_wrong_scope: Optional[str] = None, + test_wrong_scope: str | None = None, test_missing_scope: bool = False, ): """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/oir_api_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/oir_api_validator.py index cd9ccc3235..74e35260cc 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/oir_api_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/oir_api_validator.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Optional from implicitdict import ImplicitDict, StringBasedDateTime from uas_standards.astm.f3548.v21.api import ( @@ -37,7 +36,7 @@ def __init__( planning_area_volume4d: Volume4D, no_auth_session: UTMClientSession, invalid_token_session: UTMClientSession, - test_wrong_scope: Optional[str] = None, + test_wrong_scope: str | None = None, test_missing_scope: bool = False, ): """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py index 522fca3a72..87543818af 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/authentication/sub_api_validator.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Optional from uas_standards.astm.f3548.v21.api import ( OPERATIONS, @@ -34,7 +33,7 @@ def __init__( planning_area_volume4d: Volume4D, no_auth_session: UTMClientSession, invalid_token_session: UTMClientSession, - test_wrong_scope: Optional[str] = None, + test_wrong_scope: str | None = None, test_missing_scope: bool = False, ): """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/dss_interoperability.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/dss_interoperability.py index 928f4371fa..d8848e734b 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/dss_interoperability.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/dss_interoperability.py @@ -1,6 +1,5 @@ import ipaddress import socket -from typing import List, Optional from urllib.parse import urlparse from uas_standards.astm.f3548.v21.api import Volume4D @@ -22,7 +21,7 @@ class DSSInteroperability(TestScenario): _dss_primary: DSSInstance - _dss_others: List[DSSInstance] + _dss_others: list[DSSInstance] _allow_private_addresses: bool = False _valid_search_area: Volume4D @@ -32,7 +31,7 @@ def __init__( primary_dss_instance: DSSInstanceResource, all_dss_instances: DSSInstancesResource, planning_area: PlanningAreaResource, - test_exclusions: Optional[TestExclusionsResource] = None, + test_exclusions: TestExclusionsResource | None = None, ): super().__init__() scopes = { @@ -73,7 +72,7 @@ def _test_env_reqs(self): ip_addr = socket.gethostbyname(parsed_url.hostname) # We would typically get a socket.gaierror if the host does not resolve, # but we catch its parent class socket.error to cover a possibly wider range of issues - except socket.error as e: + except OSError as e: check.record_failed( summary=f"Could not resolve DSS host {parsed_url.netloc}", details=f"Could not resolve DSS host {parsed_url.netloc}: {e}", diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/__init__.py index 01fcd80d71..31876bb09a 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/__init__.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/__init__.py @@ -1,5 +1,3 @@ -from typing import Optional - from uas_standards.astm.f3548.v21.api import EntityID, SubscriptionID from monitoring.monitorlib.fetch import QueryError @@ -19,8 +17,8 @@ def step_oir_has_correct_subscription( scenario: TestScenarioType, dss: DSSInstance, oir_id: EntityID, - expected_sub_id: Optional[SubscriptionID], - step_name: Optional[str] = None, + expected_sub_id: SubscriptionID | None, + step_name: str | None = None, ): """ Ensure that an OIR is currently attached to the specified subscription, @@ -53,7 +51,7 @@ def check_oir_has_correct_subscription( scenario: TestScenarioType, dss: DSSInstance, oir_id: EntityID, - expected_sub_id: Optional[SubscriptionID], + expected_sub_id: SubscriptionID | None, ): with scenario.check( "Get operational intent reference by ID", dss.participant_id diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/crud/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/crud/__init__.py index 8d979efe9a..30ec282fdb 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/crud/__init__.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/oir/crud/__init__.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from uas_standards.astm.f3548.v21.api import ( EntityID, OperationalIntentReference, @@ -17,9 +15,9 @@ def create_oir_query( dss: DSSInstance, oir_id: EntityID, oir_params: PutOperationalIntentReferenceParameters, -) -> Tuple[ +) -> tuple[ OperationalIntentReference, - List[SubscriberToNotify], + list[SubscriberToNotify], Query, ]: """ @@ -59,9 +57,9 @@ def update_oir_query( oir_id: EntityID, oir_params: PutOperationalIntentReferenceParameters, ovn: str, -) -> Tuple[ +) -> tuple[ OperationalIntentReference, - List[SubscriberToNotify], + list[SubscriberToNotify], Query, ]: """ @@ -97,7 +95,7 @@ def update_oir_query( def delete_oir_query( scenario: TestScenarioType, dss: DSSInstance, oir_id: EntityID, ovn: str -) -> Tuple[OperationalIntentReference, List[SubscriberToNotify], Query]: +) -> tuple[OperationalIntentReference, list[SubscriberToNotify], Query]: """Issue a request to delete an OIR to the DSS instance, wrapped in a check documented in `delete_query.md`.""" with scenario.check( "Delete operational intent reference query succeeds", dss.participant_id @@ -119,7 +117,7 @@ def delete_oir_query( def get_oir_query( scenario: TestScenarioType, dss: DSSInstance, oir_id: EntityID -) -> Tuple[OperationalIntentReference, Query]: +) -> tuple[OperationalIntentReference, Query]: """ Issue a request to get an OIR by ID to the DSS instance, wrapped in a check documented in `get_query.md`. """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/sub/crud/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/sub/crud/__init__.py index 861c7af63c..f9a0c262aa 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/sub/crud/__init__.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/fragments/sub/crud/__init__.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from uas_standards.astm.f3548.v21.api import OperationalIntentReference, Subscription from monitoring.monitorlib.fetch.rid import FetchedSubscription @@ -15,7 +13,7 @@ def sub_create_query( scenario: TestScenarioType, dss: DSSInstance, sub_params: SubscriptionParams, -) -> Tuple[Subscription, List[OperationalIntentReference], MutatedSubscription]: +) -> tuple[Subscription, list[OperationalIntentReference], MutatedSubscription]: """Implements check documented in `create_query.md`.""" with scenario.check( "Create subscription query succeeds", @@ -38,7 +36,7 @@ def sub_get_query( scenario: TestScenarioType, dss: DSSInstance, sub_id: str, -) -> Tuple[Subscription, FetchedSubscription]: +) -> tuple[Subscription, FetchedSubscription]: with scenario.check("Get Subscription by ID", dss.participant_id) as check: fetched_sub = dss.get_subscription(sub_id) scenario.record_query(fetched_sub) @@ -58,7 +56,7 @@ def sub_delete_query( dss: DSSInstance, sub_id: str, sub_version: str, -) -> Tuple[Subscription, List[OperationalIntentReference], MutatedSubscription]: +) -> tuple[Subscription, list[OperationalIntentReference], MutatedSubscription]: """Implements check documented in `delete_query.md`.""" with scenario.check( "Subscription can be deleted", diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_explicit_sub_handling.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_explicit_sub_handling.py index fe304a57a2..2c8d95ea18 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_explicit_sub_handling.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_explicit_sub_handling.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Optional from uas_standards.astm.f3548.v21.api import ( EntityID, @@ -48,16 +47,16 @@ class OIRExplicitSubHandling(TestScenario): _extra_sub_id: SubscriptionID # Keep track of the current OIR state - _current_oir: Optional[OperationalIntentReference] + _current_oir: OperationalIntentReference | None _expected_manager: str _planning_area: PlanningAreaSpecification _planning_area_volume4d: Volume4D # Keep track of the current subscription - _sub_params: Optional[SubscriptionParams] - _current_sub: Optional[Subscription] + _sub_params: SubscriptionParams | None + _current_sub: Subscription | None - _current_extra_sub: Optional[Subscription] + _current_extra_sub: Subscription | None def __init__( self, @@ -372,7 +371,7 @@ def _step_update_oir_with_sufficient_explicit_sub(self, is_replacement: bool): self.end_test_step() def _step_oir_has_correct_subscription( - self, expected_sub_id: Optional[SubscriptionID], step_name: Optional[str] = None + self, expected_sub_id: SubscriptionID | None, step_name: str | None = None ): step_oir_has_correct_subscription( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_implicit_sub_handling.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_implicit_sub_handling.py index 78a3a0d22a..b0a1b64e16 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_implicit_sub_handling.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/oir_implicit_sub_handling.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import List, Optional, Set, Tuple import arrow from uas_standards.astm.f3548.v21.api import ( @@ -61,8 +60,8 @@ class OIRImplicitSubHandling(TestScenario): _sub_id: str - _oir_a_ovn: Optional[str] - _oir_b_ovn: Optional[str] + _oir_a_ovn: str | None + _oir_b_ovn: str | None # Reference times for the subscriptions and operational intents _time_0: datetime @@ -73,10 +72,10 @@ class OIRImplicitSubHandling(TestScenario): _manager: str # Keeps track of existing subscriptions in the planning area - _initial_subscribers: List[SubscriberToNotify] - _implicit_sub_1: Optional[Subscription] - _implicit_sub_2: Optional[Subscription] - _explicit_sub: Optional[Subscription] + _initial_subscribers: list[SubscriberToNotify] + _implicit_sub_1: Subscription | None + _implicit_sub_2: Subscription | None + _explicit_sub: Subscription | None def __init__( self, @@ -391,10 +390,10 @@ def _create_oir( relevant_ovns, with_implicit_sub, subscription_id=None, - ) -> Tuple[ + ) -> tuple[ OperationalIntentReference, - List[SubscriberToNotify], - Optional[Subscription], + list[SubscriberToNotify], + Subscription | None, Query, ]: """ @@ -1154,7 +1153,7 @@ def check_sub_not_none(oir): self.end_test_step() def _check_oir_has_correct_subscription( - self, oir_id: EntityID, expected_sub_id: Optional[SubscriptionID] + self, oir_id: EntityID, expected_sub_id: SubscriptionID | None ): check_oir_has_correct_subscription( self, @@ -1240,7 +1239,7 @@ def cleanup(self): self.end_cleanup() -def to_sub_ids(subscribers: List[SubscriberToNotify]) -> Set[SubscriptionID]: +def to_sub_ids(subscribers: list[SubscriberToNotify]) -> set[SubscriptionID]: """Flatten the passed list of subscribers to notify to a set of subscription IDs""" sub_ids = set() for subscriber in subscribers: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_access_control.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_access_control.py index d3fb9d9c7b..a8c027f3ee 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_access_control.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_access_control.py @@ -1,5 +1,3 @@ -from typing import Dict, List - import arrow from uas_standards.astm.f3548.v21 import api as f3548v21 from uas_standards.astm.f3548.v21.constants import Scope @@ -44,10 +42,10 @@ class OpIntentReferenceAccessControl(TestScenario): # The DSS under test _dss: DSSInstance - _pid: List[str] + _pid: list[str] # Participant IDs of users using this DSS instance - _uids: List[str] + _uids: list[str] # The same DSS, available via a separate auth adapter _dss_separate_creds: DSSInstance @@ -152,7 +150,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() - def _get_extents(self, times: Dict[TimeDuringTest, Time]) -> Volume4D: + def _get_extents(self, times: dict[TimeDuringTest, Time]) -> Volume4D: extents = Volume4DCollection() for info in (self._flight_1.resolve(times), self._flight_2.resolve(times)): extents.extend(info.basic_information.area) @@ -232,7 +230,7 @@ def _clean_known_op_intents_ids(self): ) def _attempt_to_delete_remaining_op_intents( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): """Search for op intents and attempt to delete them using the main credentials""" @@ -317,7 +315,7 @@ def _attempt_to_delete_remaining_op_intents( query_timestamps=[dq.request.timestamp], ) - def _ensure_clean_workspace(self, times: Dict[TimeDuringTest, Time]) -> bool: + def _ensure_clean_workspace(self, times: dict[TimeDuringTest, Time]) -> bool: """ Tries to provide a clean workspace. If it fails to do so and the underlying check has a severity below HIGH, this function will return false. @@ -367,7 +365,7 @@ def _ensure_clean_workspace(self, times: Dict[TimeDuringTest, Time]) -> bool: return True - def _create_op_intents(self, times: Dict[TimeDuringTest, Time]): + def _create_op_intents(self, times: dict[TimeDuringTest, Time]): flight_1 = self._flight_1.resolve(times) with self.check( "Can create an operational intent with valid credentials", self._pid @@ -434,7 +432,7 @@ def _ensure_credentials_are_different(self): ) def _check_mutation_on_non_owned_intent_fails( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): flight_1 = self._flight_1.resolve(times) with self.check( diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_key_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_key_validation.py index 0a0c795987..364fb36514 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_key_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_key_validation.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Dict, List from uas_standards.astm.f3548.v21.api import ( AirspaceConflictResponse, @@ -48,10 +47,10 @@ class OIRKeyValidation(TestScenario): _dss: DSSInstance - _oir_ids: List[EntityID] + _oir_ids: list[EntityID] # Keep track of the current OIR state - _current_oirs: Dict[EntityID, OperationalIntentReference] + _current_oirs: dict[EntityID, OperationalIntentReference] def __init__( self, @@ -174,7 +173,7 @@ def _steps_create_non_overlapping_oirs(self): self.end_test_step() def _attempt_creation_expect_conflict( - self, oir_id: EntityID, oir_params, conflicting_ids: List[EntityID] + self, oir_id: EntityID, oir_params, conflicting_ids: list[EntityID] ): with self.check( "Create operational intent reference with missing OVN fails", self._pid @@ -205,7 +204,7 @@ def _attempt_update_expect_conflict( self, oir_id: EntityID, oir_params, - conflicting_ids: List[EntityID], + conflicting_ids: list[EntityID], ovn: EntityID, ): with self.check( @@ -315,7 +314,7 @@ def _steps_attempt_create_overlapping_oir(self): self._current_oirs[third_oir.id] = third_oir def _validate_conflict_response( - self, conflicting_ids: List[EntityID], query: fetch.Query + self, conflicting_ids: list[EntityID], query: fetch.Query ): """Checks that the conflict response body is as specified. If the missing_operational_intents field is defined, its content is checked against the list of passed conflicting ids. @@ -432,7 +431,7 @@ def cleanup(self): def _expect_conflict_code( - check: PendingCheck, conflicting_ids: List[EntityID], query: fetch.Query + check: PendingCheck, conflicting_ids: list[EntityID], query: fetch.Query ): if query.status_code != 409: check.record_failed( diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_simple.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_simple.py index 027ee76ede..0092bc3cac 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_simple.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_simple.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Optional from uas_standards.astm.f3548.v21.api import ( EntityID, @@ -48,8 +47,8 @@ class OIRSimple(TestScenario): _oir_id: EntityID # Keep track of the current OIR state - _current_oir: Optional[OperationalIntentReference] - _current_oir_params: Optional[PutOperationalIntentReferenceParameters] + _current_oir: OperationalIntentReference | None + _current_oir_params: PutOperationalIntentReferenceParameters | None _expected_manager: str _planning_area: PlanningAreaSpecification _planning_area_volume4d: Volume4D @@ -349,7 +348,7 @@ def _test_params_for_current_time(self): ) def _default_oir_params( - self, subscription_id: Optional[SubscriptionID] + self, subscription_id: SubscriptionID | None ) -> PutOperationalIntentReferenceParameters: return self._planning_area.get_new_operational_intent_ref_params( key=[], diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_state_transitions.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_state_transitions.py index 23a3e6697e..1c664cdc14 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_state_transitions.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/op_intent_ref_state_transitions.py @@ -1,5 +1,3 @@ -from typing import Dict, List - import arrow from uas_standards.astm.f3548.v21 import api as f3548v21 from uas_standards.astm.f3548.v21.api import OperationalIntentState @@ -42,10 +40,10 @@ class OpIntentReferenceStateTransitions(TestScenario): # The DSS under test _dss: DSSInstance - _pid: List[str] + _pid: list[str] # Participant IDs of users using this DSS instance - _uids: List[str] + _uids: list[str] _flight: FlightInfoTemplate @@ -118,7 +116,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() - def _get_extents(self, times: Dict[TimeDuringTest, Time]) -> Volume4D: + def _get_extents(self, times: dict[TimeDuringTest, Time]) -> Volume4D: return self._flight.resolve(times).basic_information.area.bounding_volume def _clean_known_op_intents_ids(self): @@ -156,7 +154,7 @@ def _clean_known_op_intents_ids(self): ) def _attempt_to_delete_remaining_op_intents( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): """Search for op intents and attempt to delete them""" @@ -198,7 +196,7 @@ def _attempt_to_delete_remaining_op_intents( query_timestamps=e.query_timestamps, ) - def _ensure_clean_workspace(self, times: Dict[TimeDuringTest, Time]) -> bool: + def _ensure_clean_workspace(self, times: dict[TimeDuringTest, Time]) -> bool: """ Tries to provide a clean workspace. If it fails to do so and the underlying check has a severity below HIGH, this function will return false. @@ -242,7 +240,7 @@ def _ensure_clean_workspace(self, times: Dict[TimeDuringTest, Time]) -> bool: return True - def _check_unauthorized_state_creation(self, times: Dict[TimeDuringTest, Time]): + def _check_unauthorized_state_creation(self, times: dict[TimeDuringTest, Time]): times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime) # Reuse info from flight 1 for the third Operational Intent Ref flight_3 = self._flight.resolve(times) @@ -305,7 +303,7 @@ def _check_unauthorized_state_creation(self, times: Dict[TimeDuringTest, Time]): ) def _steps_check_unauthorized_state_transitions( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): """This checks for UNAUTHORIZED state transitions, that is, transitions that require the correct scope, but are not otherwise disallowed by the standard.""" diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions.py index 98a9db1d45..3d23d48b85 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions.py @@ -1,5 +1,4 @@ from datetime import UTC, datetime, timedelta -from typing import Dict, List, Set from uas_standards.astm.f3548.v21.api import ( EntityID, @@ -47,11 +46,11 @@ class SubscriptionInteractions(TestScenario): _background_sub_id: SubscriptionID - _oir_ids: List[EntityID] - _sub_ids: List[SubscriptionID] + _oir_ids: list[EntityID] + _sub_ids: list[SubscriptionID] - _current_subs: Dict[SubscriptionID, Subscription] - _current_oirs: Dict[EntityID, OperationalIntentReference] + _current_subs: dict[SubscriptionID, Subscription] + _current_oirs: dict[EntityID, OperationalIntentReference] # Reference times for the subscriptions and operational intents _time_start: datetime @@ -162,7 +161,7 @@ def _expected_subs_check( ) def _implicit_subs_check( - _participants: List[ParticipantID], + _participants: list[ParticipantID], _notif_ids: set[str], _query_timestamp: datetime, ): @@ -182,7 +181,7 @@ def _implicit_subs_check( ) self.begin_test_step("Create an OIR at every DSS in sequence") - possible_culprits: List[ParticipantID] = [] + possible_culprits: list[ParticipantID] = [] for i, dss in enumerate([self._dss] + self._secondary_instances): oir_id = self._oir_ids[i] oir = self._planning_area.get_new_operational_intent_ref_params( @@ -462,7 +461,7 @@ def cleanup(self): self.end_cleanup() -def to_sub_ids(subscribers: List[SubscriberToNotify]) -> Set[SubscriptionID]: +def to_sub_ids(subscribers: list[SubscriberToNotify]) -> set[SubscriptionID]: """Flatten the passed list of subscribers to notify to a set of subscription IDs""" sub_ids = set() for subscriber in subscribers: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions_deletion.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions_deletion.py index 4ba7b0b182..f142f837dc 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions_deletion.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_interactions_deletion.py @@ -1,5 +1,4 @@ from datetime import UTC, datetime, timedelta -from typing import Dict, List from uas_standards.astm.f3548.v21.api import ( EntityID, @@ -34,11 +33,11 @@ class SubscriptionInteractionsDeletion(TestScenario): - _oir_ids: List[EntityID] - _sub_ids: List[SubscriptionID] + _oir_ids: list[EntityID] + _sub_ids: list[SubscriptionID] - _current_subs: Dict[SubscriptionID, Subscription] - _current_oirs: Dict[EntityID, OperationalIntentReference] + _current_subs: dict[SubscriptionID, Subscription] + _current_oirs: dict[EntityID, OperationalIntentReference] _time_start: datetime _time_end: datetime diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_simple.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_simple.py index a135bd603a..e62d545944 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_simple.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_simple.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Dict, List from uas_standards.astm.f3548.v21.api import Subscription, SubscriptionID from uas_standards.astm.f3548.v21.constants import Scope @@ -46,16 +45,16 @@ class SubscriptionSimple(TestScenario): # Base identifier for the subscriptions that will be created _base_sub_id: SubscriptionID - _test_subscription_ids: List[SubscriptionID] + _test_subscription_ids: list[SubscriptionID] # Base parameters used for subscription creation variations _sub_generation_params: SubscriptionParams # Effective parameters used for each subscription, indexed by subscription ID - _sub_params_by_sub_id: Dict[SubscriptionID, SubscriptionParams] + _sub_params_by_sub_id: dict[SubscriptionID, SubscriptionParams] # Keep track of the latest subscription returned by the DSS - _current_subscriptions: Dict[SubscriptionID, Subscription] + _current_subscriptions: dict[SubscriptionID, Subscription] # An area designed to be too big to be allowed to search by the DSS _problematically_big_area_vol: Polygon @@ -581,7 +580,7 @@ def _validate_subscription_and_notif_index( sub_under_test: Subscription, creation_params: SubscriptionParams, was_mutated: bool, - query_timestamps: List[datetime], + query_timestamps: list[datetime], ): """Compare the passed subscription with the data we specified when creating it""" self._validate_subscription( @@ -612,7 +611,7 @@ def _validate_subscription( sub_under_test: Subscription, creation_params: SubscriptionParams, was_mutated: bool, - query_timestamps: List[datetime], + query_timestamps: list[datetime], ): """ Validate the subscription against the parameters used to create it. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_validation.py index 265e3eb756..16ad6574cc 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/subscription_validation.py @@ -1,5 +1,5 @@ from datetime import UTC, datetime, timedelta -from typing import Any, Dict +from typing import Any from uas_standards.astm.f3548.v21.constants import ( DSSMaxSubscriptionDurationHours, @@ -43,7 +43,7 @@ class SubscriptionValidation(TestScenario): _planning_area_volume4d: Volume4D - _sub_generation_kwargs: Dict[str, Any] + _sub_generation_kwargs: dict[str, Any] """ Parameters used to create subscriptions. `subscription_id`: ID of F3548-21 subscription to create/modify. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/constraint_ref_synchronization.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/constraint_ref_synchronization.py index 1bf27724c5..cc462cdcb0 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/constraint_ref_synchronization.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/constraint_ref_synchronization.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import List, Optional from uas_standards.astm.f3548.v21.api import ( ConstraintReference, @@ -51,7 +50,7 @@ class CRSynchronization(TestScenario): _dss: DSSInstance - _secondary_dss_instances: List[DSSInstance] + _secondary_dss_instances: list[DSSInstance] # Base identifier for the OIR that will be created _cr_id: EntityID @@ -60,7 +59,7 @@ class CRSynchronization(TestScenario): _cr_params: PutConstraintReferenceParameters # Keep track of the current OIR state - _current_cr: Optional[ConstraintReference] + _current_cr: ConstraintReference | None _expected_manager: str @@ -307,7 +306,7 @@ def _search_secondaries_and_compare( "Propagated constraint reference general area is synchronized", involved_participants, ) as check: - cr: Optional[ConstraintReference] = next( + cr: ConstraintReference | None = next( (_cr for _cr in crs if _cr.id == self._cr_id), None ) if cr is None: @@ -332,7 +331,7 @@ def _validate_cr_from_secondary( q: Query, expected_cr_params: PutConstraintReferenceParameters, main_check_name: str, - involved_participants: List[str], + involved_participants: list[str], from_search: bool = False, ): with self.check(main_check_name, involved_participants) as main_check: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/op_intent_ref_synchronization.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/op_intent_ref_synchronization.py index 1b34b79aeb..92b630af25 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/op_intent_ref_synchronization.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/op_intent_ref_synchronization.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import List, Optional from uas_standards.astm.f3548.v21.api import ( EntityID, @@ -50,7 +49,7 @@ class OIRSynchronization(TestScenario): _dss: DSSInstance - _dss_read_instances: List[DSSInstance] + _dss_read_instances: list[DSSInstance] # Base identifier for the OIR that will be created _oir_id: EntityID @@ -59,7 +58,7 @@ class OIRSynchronization(TestScenario): _oir_params: PutOperationalIntentReferenceParameters # Keep track of the current OIR state - _current_oir: Optional[OperationalIntentReference] + _current_oir: OperationalIntentReference | None _expected_manager: str @@ -311,7 +310,7 @@ def _search_secondaries_and_compare( "Propagated operational intent reference general area is synchronized", involved_participants, ) as check: - oir: Optional[OperationalIntentReference] = None + oir: OperationalIntentReference | None = None for _oir in oirs: if _oir.id == self._oir_id: oir = _oir @@ -341,7 +340,7 @@ def _validate_oir_from_secondary( q: Query, expected_oir_params: PutOperationalIntentReferenceParameters, main_check_name: str, - involved_participants: List[str], + involved_participants: list[str], ): # TODO: this main check mechanism may be removed if we are able to specify requirements to be validated in test step fragments diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py index 7af4ea03e5..6dbc2f5f81 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/subscription_synchronization.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Dict, List, Optional import loguru from uas_standards.astm.f3548.v21.api import Subscription, SubscriptionID @@ -52,15 +51,15 @@ class SubscriptionSynchronization(TestScenario): _dss: DSSInstance # Separate DSS client for testing manager synchronization - _dss_separate_creds: Optional[DSSInstance] + _dss_separate_creds: DSSInstance | None - _dss_read_instances: List[DSSInstance] + _dss_read_instances: list[DSSInstance] # Base identifier for the subscriptions that will be created _sub_id: SubscriptionID # Extra sub IDs for testing only deletions - _ids_for_deletion: List[SubscriptionID] + _ids_for_deletion: list[SubscriptionID] # Extra sub id for testing manager sync _acl_sub_id: SubscriptionID @@ -71,10 +70,10 @@ class SubscriptionSynchronization(TestScenario): _sub_params: SubscriptionParams # Keep track of the current subscription state - _current_subscription = Optional[Subscription] + _current_subscription = Subscription | None # For the secondary deletion test - _subs_for_deletion: Dict[SubscriptionID, Subscription] + _subs_for_deletion: dict[SubscriptionID, Subscription] def __init__( self, @@ -82,7 +81,7 @@ def __init__( other_instances: DSSInstancesResource, id_generator: IDGeneratorResource, planning_area: PlanningAreaResource, - second_utm_auth: Optional[AuthAdapterResource] = None, + second_utm_auth: AuthAdapterResource | None = None, ): """ Args: @@ -308,7 +307,7 @@ def _validate_sub_area_from_secondary( self, secondary_dss: DSSInstance, expected_sub_id: str, - involved_participants: List[str], + involved_participants: list[str], ): """Checks that the secondary DSS is also aware of the proper subscription's area: - searching for the subscription's area should yield the subscription @@ -371,7 +370,7 @@ def _validate_get_sub_from_secondary( self, secondary_dss: DSSInstance, expected_sub_params: SubscriptionParams, - involved_participants: List[str], + involved_participants: list[str], ): """Fetches the subscription from the secondary DSS and validates it.""" with self.check( @@ -795,7 +794,7 @@ def _confirm_dss_has_no_sub( self, dss_instance: DSSInstance, sub_id: str, - other_participant_id: Optional[str], + other_participant_id: str | None, ): """Confirm that a DSS has no subscription. other_participant_id may be specified if a failed check may be caused by it.""" diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/uss_availability_synchronization.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/uss_availability_synchronization.py index cb1e208579..aa430feb31 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/uss_availability_synchronization.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/synchronization/uss_availability_synchronization.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from uas_standards.astm.f3548.v21.api import UssAvailabilityState from uas_standards.astm.f3548.v21.constants import Scope @@ -28,11 +26,11 @@ class USSAvailabilitySynchronization(TestScenario): _dss: DSSInstance - _dss_read_instances: List[DSSInstance] + _dss_read_instances: list[DSSInstance] _uss_id: str - _current_version: Optional[str] = None + _current_version: str | None = None def __init__( self, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py index fa1ad95ab0..9886bea23d 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/test_step_fragments.py @@ -1,5 +1,3 @@ -from typing import Optional - from uas_standards.astm.f3548.v21.api import EntityID, Volume4D from monitoring.monitorlib import fetch @@ -65,7 +63,7 @@ def remove_constraint_ref( def cleanup_sub( scenario: TestScenarioType, dss: DSSInstance, sub_id: EntityID -) -> Optional[MutatedSubscription]: +) -> MutatedSubscription | None: """Cleanup a subscription at the DSS. Does not fail if it is not found. :return: the DSS response if the subscription exists diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/__init__.py index 9174334ae7..8f2c6f0788 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/__init__.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/__init__.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List from monitoring.monitorlib import schema_validation from monitoring.uss_qualifier.scenarios.scenario import PendingCheck @@ -7,7 +6,7 @@ def fail_with_schema_errors( check: PendingCheck, - errors: List[schema_validation.ValidationError], + errors: list[schema_validation.ValidationError], t_dss: datetime, ) -> None: """ diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/cr_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/cr_validator.py index 4d5b23ecd8..7d9079acbe 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/cr_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/cr_validator.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List, Optional from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21.api import ( @@ -42,8 +41,8 @@ class ConstraintReferenceValidator: Scenario in which this validator is being used. Will be used to register checks. """ - _cr_params: Optional[PutConstraintReferenceParameters] - _pid: List[str] + _cr_params: PutConstraintReferenceParameters | None + _pid: list[str] """Participant ID(s) to use for the checks""" def __init__( @@ -51,8 +50,8 @@ def __init__( main_check: PendingCheck, scenario: TestScenario, expected_manager: str, - participant_id: List[str], - cr_params: Optional[PutConstraintReferenceParameters], + participant_id: list[str], + cr_params: PutConstraintReferenceParameters | None, ): self._main_check = main_check self._scenario = scenario @@ -92,10 +91,10 @@ def _validate_cr( expected_entity_id: EntityID, dss_cr: ConstraintReference, t_dss: datetime, - previous_version: Optional[int], - expected_version: Optional[int], - previous_ovn: Optional[str], - expected_ovn: Optional[str], + previous_version: int | None, + expected_version: int | None, + previous_ovn: str | None, + expected_ovn: str | None, ) -> None: """ Args: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/oir_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/oir_validator.py index 7f7d588a6e..8381ed2a16 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/oir_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/oir_validator.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List, Optional from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21.api import ( @@ -42,8 +41,8 @@ class OIRValidator: Scenario in which this validator is being used. Will be used to register checks. """ - _oir_params: Optional[PutOperationalIntentReferenceParameters] - _pid: List[str] + _oir_params: PutOperationalIntentReferenceParameters | None + _pid: list[str] """Participant ID(s) to use for the checks""" def __init__( @@ -51,8 +50,8 @@ def __init__( main_check: PendingCheck, scenario: TestScenario, expected_manager: str, - participant_id: List[str], - oir_params: Optional[PutOperationalIntentReferenceParameters], + participant_id: list[str], + oir_params: PutOperationalIntentReferenceParameters | None, ): self._main_check = main_check self._scenario = scenario @@ -92,10 +91,10 @@ def _validate_oir( expected_entity_id: EntityID, dss_oir: OperationalIntentReference, t_dss: datetime, - previous_version: Optional[int], - expected_version: Optional[int], - previous_ovn: Optional[str], - expected_ovn: Optional[str], + previous_version: int | None, + expected_version: int | None, + previous_ovn: str | None, + expected_ovn: str | None, ) -> None: """ Args: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/subscription_validator.py b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/subscription_validator.py index 94adfde639..2cbe166bbf 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/subscription_validator.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/dss/validators/subscription_validator.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List, Optional from uas_standards.astm.f3548.v21.api import Subscription, SubscriptionID @@ -35,16 +34,16 @@ class SubscriptionValidator: Scenario in which this validator is being used. Will be used to register checks. """ - _sub_params: Optional[SubscriptionParams] - _pid: List[str] + _sub_params: SubscriptionParams | None + _pid: list[str] """Participant ID(s) to use for the checks""" def __init__( self, main_check: PendingCheck, scenario: TestScenario, - participant_id: List[str], - sub_params: Optional[SubscriptionParams], + participant_id: list[str], + sub_params: SubscriptionParams | None, ): self._main_check = main_check self._scenario = scenario @@ -78,8 +77,8 @@ def _validate_sub( dss_sub: Subscription, t_dss: datetime, is_implicit: bool, - previous_version: Optional[str], - expected_version: Optional[str], + previous_version: str | None, + expected_version: str | None, ) -> None: """ Args: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/evaluation.py b/monitoring/uss_qualifier/scenarios/astm/utm/evaluation.py index ce8ef15cad..3c1b9fc219 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/evaluation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/evaluation.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import List, Optional from uas_standards.astm.f3548.v21.api import OperationalIntentDetails, Volume4D @@ -13,16 +12,14 @@ def validate_op_intent_details( op_intent_details: OperationalIntentDetails, expected_priority: int, expected_extent: Volume4D, -) -> Optional[str]: - errors_text: List[str] = [] +) -> str | None: + errors_text: list[str] = [] # Check that the USS is providing matching priority actual_priority = priority_of(op_intent_details) if actual_priority != expected_priority: errors_text.append( - "Priority specified by USS in operational intent details ({}) is different than the priority in the injected flight ({})".format( - actual_priority, expected_priority - ) + f"Priority specified by USS in operational intent details ({actual_priority}) is different than the priority in the injected flight ({expected_priority})" ) # Check that the USS is providing reasonable volume 4D @@ -39,31 +36,23 @@ def validate_op_intent_details( resp_end = vol4c.time_end.datetime if resp_alts[0] > expected_extent.volume.altitude_lower.value + NUMERIC_PRECISION: errors_text.append( - "Lower altitude specified by USS in operational intent details ({} m WGS84) is above the lower altitude in the injected flight ({} m WGS84)".format( - resp_alts[0], expected_extent.volume.altitude_lower.value - ) + f"Lower altitude specified by USS in operational intent details ({resp_alts[0]} m WGS84) is above the lower altitude in the injected flight ({expected_extent.volume.altitude_lower.value} m WGS84)" ) elif resp_alts[1] < expected_extent.volume.altitude_upper.value - NUMERIC_PRECISION: errors_text.append( - "Upper altitude specified by USS in operational intent details ({} m WGS84) is below the upper altitude in the injected flight ({} m WGS84)".format( - resp_alts[1], expected_extent.volume.altitude_upper.value - ) + f"Upper altitude specified by USS in operational intent details ({resp_alts[1]} m WGS84) is below the upper altitude in the injected flight ({expected_extent.volume.altitude_upper.value} m WGS84)" ) elif resp_start > expected_extent.time_start.value.datetime + timedelta( seconds=NUMERIC_PRECISION ): errors_text.append( - "Start time specified by USS in operational intent details ({}) is past the start time of the injected flight ({})".format( - resp_start.isoformat(), expected_extent.time_start.value - ) + f"Start time specified by USS in operational intent details ({resp_start.isoformat()}) is past the start time of the injected flight ({expected_extent.time_start.value})" ) elif resp_end < expected_extent.time_end.value.datetime - timedelta( seconds=NUMERIC_PRECISION ): errors_text.append( - "End time specified by USS in operational intent details ({}) is prior to the end time of the injected flight ({})".format( - resp_end.isoformat(), expected_extent.time_end.value - ) + f"End time specified by USS in operational intent details ({resp_end.isoformat()}) is prior to the end time of the injected flight ({expected_extent.time_end.value})" ) return "; ".join(errors_text) if len(errors_text) > 0 else None diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index a34ef448ab..fb07fcf437 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import Dict import arrow from implicitdict import StringBasedTimeDelta @@ -50,7 +49,7 @@ class FlightIntentValidation(TestScenario): ) PLAN_VALID_FLIGHT_STEP = "Plan Valid Flight" - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] valid_flight: FlightInfoTemplate valid_activated: FlightInfoTemplate diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/make_uss_report.py b/monitoring/uss_qualifier/scenarios/astm/utm/make_uss_report.py index f2a051d1cc..0aa3a496ff 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/make_uss_report.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/make_uss_report.py @@ -1,5 +1,3 @@ -from typing import Dict - import arrow from implicitdict import ImplicitDict, StringBasedDateTime from uas_standards.astm.f3548.v21 import constants @@ -54,7 +52,7 @@ def run(self, context: ExecutionContext): self.end_test_case() self.end_test_scenario() - def _get_uss_base_urls(self, context: ExecutionContext) -> Dict[str, ParticipantID]: + def _get_uss_base_urls(self, context: ExecutionContext) -> dict[str, ParticipantID]: base_urls_by_participant = {} for report in context.find_test_scenario_reports(FlightIntentValidation): cases = [ @@ -112,7 +110,7 @@ def _get_uss_base_urls(self, context: ExecutionContext) -> Dict[str, Participant return base_urls_by_participant - def _call_make_uss_report(self, base_urls: Dict[str, ParticipantID]) -> None: + def _call_make_uss_report(self, base_urls: dict[str, ParticipantID]) -> None: for base_url, participant_id in base_urls.items(): client = UTMClientSession(base_url, self._auth) url = base_url + OPERATIONS[OperationID.MakeUssReport].path diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/assets/make_assets.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/assets/make_assets.py index 96c2b781ab..d0b905cd35 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/assets/make_assets.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/assets/make_assets.py @@ -2,12 +2,11 @@ import inspect from textwrap import dedent -from typing import List import svg -def translate(points: List[float], dx: float, dy: float) -> List[float]: +def translate(points: list[float], dx: float, dy: float) -> list[float]: result = [] x = True for p in points: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py index 478bfa211c..dd2267f414 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - import arrow from uas_standards.astm.f3548.v21.api import OperationalIntentReference from uas_standards.astm.f3548.v21.constants import Scope @@ -53,16 +51,16 @@ class ConflictEqualPriorityNotPermitted(TestScenario): - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] - flight1_id: Optional[str] = None + flight1_id: str | None = None flight1_planned: FlightInfoTemplate flight1_activated: FlightInfoTemplate flight1m_activated: FlightInfoTemplate flight1c_planned: FlightInfoTemplate flight1c_activated: FlightInfoTemplate - flight2_id: Optional[str] = None + flight2_id: str | None = None flight2m_planned: FlightInfoTemplate flight2_planned: FlightInfoTemplate flight2_activated: FlightInfoTemplate @@ -77,7 +75,7 @@ def __init__( tested_uss: FlightPlannerResource, control_uss: FlightPlannerResource, dss: DSSInstanceResource, - flight_intents: Optional[FlightIntentsResource] = None, + flight_intents: FlightIntentsResource | None = None, ): super().__init__() self.tested_uss = tested_uss.client @@ -307,7 +305,7 @@ def _attempt_activate_flight_conflict(self): def _attempt_modify_planned_flight_conflict( self, - ) -> Optional[OperationalIntentReference]: + ) -> OperationalIntentReference | None: self.begin_test_step("Plan Flight 1c") flight1c_planned = self.resolve_flight(self.flight1c_planned) @@ -350,8 +348,8 @@ def _attempt_modify_planned_flight_conflict( return flight_1_oi_ref def _attempt_modify_activated_flight_conflict( - self, flight_1_oi_ref: Optional[OperationalIntentReference] - ) -> Optional[OperationalIntentReference]: + self, flight_1_oi_ref: OperationalIntentReference | None + ) -> OperationalIntentReference | None: self.begin_test_step("Activate Flight 1c") flight1c_activated = self.resolve_flight(self.flight1c_activated) @@ -401,7 +399,7 @@ def _attempt_modify_activated_flight_conflict( def _modify_activated_flight_preexisting_conflict( self, - flight_1_oi_ref: Optional[OperationalIntentReference], + flight_1_oi_ref: OperationalIntentReference | None, ): self.begin_test_step("Activate Flight 1") flight1_activated = self.resolve_flight(self.flight1_activated) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/assets/make_assets.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/assets/make_assets.py index a210ab9432..4493a1918d 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/assets/make_assets.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/assets/make_assets.py @@ -2,12 +2,11 @@ import inspect from textwrap import dedent -from typing import List import svg -def translate(points: List[float], dx: float, dy: float) -> List[float]: +def translate(points: list[float], dx: float, dy: float) -> list[float]: result = [] x = True for p in points: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index 75467d7f9d..3139439e61 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional, Tuple - import arrow from uas_standards.astm.f3548.v21.api import OperationalIntentReference from uas_standards.astm.f3548.v21.constants import Scope @@ -46,16 +44,16 @@ class ConflictHigherPriority(TestScenario): - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] - flight1_id: Optional[str] = None + flight1_id: str | None = None flight1_planned: FlightInfoTemplate flight1m_planned: FlightInfoTemplate flight1_activated: FlightInfoTemplate flight1m_activated: FlightInfoTemplate flight1c_activated: FlightInfoTemplate - flight2_id: Optional[str] = None + flight2_id: str | None = None flight2_planned: FlightInfoTemplate flight2_activated: FlightInfoTemplate flight2m_activated: FlightInfoTemplate @@ -252,7 +250,7 @@ def _attempt_plan_flight_conflict(self): def _attempt_modify_planned_flight_conflict( self, - ) -> Tuple[Optional[OperationalIntentReference], FlightInfo]: + ) -> tuple[OperationalIntentReference | None, FlightInfo]: self.begin_test_step("Plan Flight 1") flight1_planned = self.resolve_flight(self.flight1_planned) @@ -312,9 +310,9 @@ def _attempt_modify_planned_flight_conflict( def _attempt_activate_flight_conflict( self, - flight_1_oi_ref: Optional[OperationalIntentReference], + flight_1_oi_ref: OperationalIntentReference | None, flight_1_intent: FlightInfo, - ) -> Optional[OperationalIntentReference]: + ) -> OperationalIntentReference | None: self.begin_test_step("Attempt to activate conflicting Flight 1") flight1_activated = self.resolve_flight(self.flight1_activated) @@ -339,8 +337,8 @@ def _attempt_activate_flight_conflict( return flight_1_oi_ref def _modify_activated_flight_conflict_preexisting( - self, flight_1_oi_ref: Optional[OperationalIntentReference] - ) -> Tuple[FlightInfo, OperationalIntentReference, OperationalIntentReference]: + self, flight_1_oi_ref: OperationalIntentReference | None + ) -> tuple[FlightInfo, OperationalIntentReference, OperationalIntentReference]: self.begin_test_step("Delete Flight 2") _ = delete_flight(self, self.control_uss, self.flight2_id) self.flight2_id = None diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/solo_happy_path.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/solo_happy_path.py index f2e248d272..2f53b4ae5e 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/solo_happy_path.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/solo_happy_path.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - import arrow from uas_standards.astm.f3548.v21.constants import Scope @@ -30,9 +28,9 @@ class SoloHappyPath(TestScenario): - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] - flight1_id: Optional[str] = None + flight1_id: str | None = None flight1_planned: FlightInfoTemplate flight1_activated: FlightInfoTemplate @@ -43,7 +41,7 @@ def __init__( self, tested_uss: FlightPlannerResource, dss: DSSInstanceResource, - flight_intents: Optional[FlightIntentsResource] = None, + flight_intents: FlightIntentsResource | None = None, ): super().__init__() self.tested_uss = tested_uss.client diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss.py b/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss.py index dfc82d2334..ab027c5578 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss.py @@ -1,5 +1,3 @@ -from typing import Dict, List, Optional - import arrow from uas_standards.astm.f3548.v21.api import ( OperationalIntentReference, @@ -50,7 +48,7 @@ class DownUSS(TestScenario): - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] flight1_planned: FlightInfoTemplate @@ -92,7 +90,7 @@ def _dss_req_scopes(self) -> dict[str, str]: } @property - def _expected_flight_intents(self) -> List[ExpectedFlightIntent]: + def _expected_flight_intents(self) -> list[ExpectedFlightIntent]: return [ ExpectedFlightIntent( "flight1_planned", @@ -175,7 +173,7 @@ def _put_conflicting_op_intent_step( self, conflicting_flight: FlightInfo, target_state: OperationalIntentState, - old_op_intent: Optional[OperationalIntentReference] = None, + old_op_intent: OperationalIntentReference | None = None, ) -> OperationalIntentReference: if old_op_intent is not None: key = [old_op_intent.ovn] diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss_equal_priority_not_permitted.py index 1cd0a81e13..3c81c5c66b 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/off_nominal_planning/down_uss_equal_priority_not_permitted.py @@ -1,5 +1,3 @@ -from typing import List - import arrow from uas_standards.astm.f3548.v21.api import ( OperationalIntentReference, @@ -54,7 +52,7 @@ def _dss_req_scopes(self) -> dict[str, str]: return scopes @property - def _expected_flight_intents(self) -> List[ExpectedFlightIntent]: + def _expected_flight_intents(self) -> list[ExpectedFlightIntent]: return [ ExpectedFlightIntent( "flight2_planned", diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py index 08a162a9a9..3c5bcc7cbe 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/prep_planners.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from uas_standards.astm.f3548.v21.api import OperationalIntentReference from uas_standards.astm.f3548.v21.constants import Scope @@ -29,12 +27,12 @@ def __init__( flight_planners: FlightPlannersResource, dss: DSSInstanceResource, flight_intents: FlightIntentsResource, - mock_uss: Optional[MockUSSResource] = None, - flight_intents2: Optional[FlightIntentsResource] = None, - flight_intents3: Optional[FlightIntentsResource] = None, - flight_intents4: Optional[FlightIntentsResource] = None, + mock_uss: MockUSSResource | None = None, + flight_intents2: FlightIntentsResource | None = None, + flight_intents3: FlightIntentsResource | None = None, + flight_intents4: FlightIntentsResource | None = None, ): - super(PrepareFlightPlanners, self).__init__( + super().__init__( flight_planners, flight_intents, mock_uss, @@ -88,7 +86,7 @@ def run(self, context): self.end_test_scenario() def _remove_my_op_intents( - self, my_op_intents: List[OperationalIntentReference] + self, my_op_intents: list[OperationalIntentReference] ) -> None: already_removed = set() for oi_ref in my_op_intents: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/assets/make_assets.py b/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/assets/make_assets.py index eac013693d..02b579ab91 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/assets/make_assets.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/assets/make_assets.py @@ -2,12 +2,11 @@ import inspect from textwrap import dedent -from typing import List import svg -def translate(points: List[float], dx: float, dy: float) -> List[float]: +def translate(points: list[float], dx: float, dy: float) -> list[float]: result = [] x = True for p in points: diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/receive_notifications_for_awareness.py b/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/receive_notifications_for_awareness.py index dcafee0986..1bd7a2ce04 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/receive_notifications_for_awareness.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/subscription_notifications/receive_notifications_for_awareness/receive_notifications_for_awareness.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - import arrow from uas_standards.astm.f3548.v21.api import OperationalIntentReference from uas_standards.astm.f3548.v21.constants import Scope @@ -63,7 +61,7 @@ def __init__( tested_uss: FlightPlannerResource, mock_uss: MockUSSResource, dss: DSSInstanceResource, - flight_intents: Optional[FlightIntentsResource] = None, + flight_intents: FlightIntentsResource | None = None, ): super().__init__() self.tested_uss_client = tested_uss.client @@ -146,7 +144,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() def _receive_notification_successfully_when_activated_test_case( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime) @@ -220,7 +218,7 @@ def _receive_notification_successfully_when_activated_test_case( self.end_test_step() def _receive_notification_successfully_when_activated_modified_test_case( - self, times: Dict[TimeDuringTest, Time] + self, times: dict[TimeDuringTest, Time] ): times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime) flight_2_planned_modified = self.flight_2_planned_modified.resolve(times) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py b/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py index a1cad628b0..3a32c4a4f9 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py @@ -1,7 +1,6 @@ from __future__ import annotations from enum import Enum -from typing import List, Optional, Set, Union from implicitdict import ImplicitDict from uas_standards.astm.f3548.v21.api import ( @@ -33,7 +32,7 @@ ) -class OpIntentValidator(object): +class OpIntentValidator: """ This class enables the validation of the sharing (or not) of an operational intent with the DSS. It does so by comparing the operational intents found @@ -46,21 +45,21 @@ class OpIntentValidator(object): _extent: Volume4D - _before_oi_refs: List[OperationalIntentReference] + _before_oi_refs: list[OperationalIntentReference] _before_query: fetch.Query - _after_oi_refs: List[OperationalIntentReference] + _after_oi_refs: list[OperationalIntentReference] _after_query: fetch.Query - _new_oi_ref: Optional[OperationalIntentReference] = None + _new_oi_ref: OperationalIntentReference | None = None def __init__( self, scenario: TestScenarioType, flight_planner: FlightPlannerClient, dss: DSSInstance, - extent: Union[Volume4D, List[Volume4D], FlightInfo, List[FlightInfo]], - orig_oi_ref: Optional[OperationalIntentReference] = None, + extent: Volume4D | list[Volume4D] | FlightInfo | list[FlightInfo], + orig_oi_ref: OperationalIntentReference | None = None, ): """ :param scenario: test scenario in which the operational intent is being validated. @@ -72,10 +71,10 @@ def __init__( self._scenario: TestScenarioType = scenario self._flight_planner: FlightPlannerClient = flight_planner self._dss: DSSInstance = dss - self._orig_oi_ref: Optional[OperationalIntentReference] = orig_oi_ref + self._orig_oi_ref: OperationalIntentReference | None = orig_oi_ref - if isinstance(extent, List): - extents_list: List[Volume4D] = [] + if isinstance(extent, list): + extents_list: list[Volume4D] = [] for extent_el in extent: if isinstance(extent_el, Volume4D): extents_list.append(extent_el) @@ -117,7 +116,7 @@ def __enter__(self) -> OpIntentValidator: def __exit__(self, exc_type, exc_val, exc_tb): pass - def _find_after_oi(self, oi_id: str) -> Optional[OperationalIntentReference]: + def _find_after_oi(self, oi_id: str) -> OperationalIntentReference | None: found = [oi_ref for oi_ref in self._after_oi_refs if oi_ref.id == oi_id] return found[0] if len(found) != 0 else None @@ -200,7 +199,7 @@ def expect_shared( self, flight_info: FlightInfo, skip_if_not_found: bool = False, - ) -> Optional[OperationalIntentReference]: + ) -> OperationalIntentReference | None: """Validate that operational intent information was correctly shared for a flight intent. This function implements the test step described in validate_shared_operational_intent.md. @@ -233,9 +232,9 @@ def expect_shared_with_invalid_data( self, flight_info: FlightInfo, validation_failure_type: OpIntentValidationFailureType, - invalid_fields: Optional[List] = None, + invalid_fields: list | None = None, skip_if_not_found: bool = False, - ) -> Optional[OperationalIntentReference]: + ) -> OperationalIntentReference | None: """Validate that operational intent information was shared with dss, but when shared with other USSes, it is expected to have specified invalid data. @@ -295,7 +294,7 @@ def _operational_intent_shared_check( self, flight_intent: FlightInfo, skip_if_not_found: bool, - ) -> Optional[OperationalIntentReference]: + ) -> OperationalIntentReference | None: with self._scenario.check( "Operational intent shared correctly", [self._flight_planner.participant_id] ) as check: @@ -498,7 +497,7 @@ def _check_op_intent_telemetry(self, oi_ref: OperationalIntentReference): def _evaluate_op_intent_validation( self, oi_full_query: fetch.Query - ) -> Set[OpIntentValidationFailure]: + ) -> set[OpIntentValidationFailure]: """Evaluates the validation failures in operational intent received""" validation_failures = set() @@ -566,9 +565,9 @@ def volume_vertices(v4): def _expected_validation_failure_found( self, - validation_failures: Set[OpIntentValidationFailure], + validation_failures: set[OpIntentValidationFailure], expected_validation_type: OpIntentValidationFailureType, - expected_invalid_fields: Optional[List[str]] = None, + expected_invalid_fields: list[str] | None = None, ) -> OpIntentValidationFailure: """ Checks if expected validation type is in validation failures @@ -591,8 +590,8 @@ def _expected_validation_failure_found( errors = failure_found.errors def expected_fields_in_errors( - fields: List[str], - errors: List[schema_validation.ValidationError], + fields: list[str], + errors: list[schema_validation.ValidationError], ) -> bool: all_found = True for field in fields: @@ -624,10 +623,10 @@ class OpIntentValidationFailureType(str, Enum): class OpIntentValidationFailure(ImplicitDict): validation_failure_type: OpIntentValidationFailureType - error_text: Optional[str] = None + error_text: str | None = None """Any error_text returned after validation check""" - errors: Optional[List[schema_validation.ValidationError]] = None + errors: list[schema_validation.ValidationError] | None = None """Any errors returned after validation check""" def __hash__(self): diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/versioning/evaluate_system_versions.py b/monitoring/uss_qualifier/scenarios/astm/utm/versioning/evaluate_system_versions.py index 2dadd1ae1f..b98626d3fb 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/versioning/evaluate_system_versions.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/versioning/evaluate_system_versions.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from typing import Dict, Tuple from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.versioning.api import GetVersionResponse @@ -16,7 +15,7 @@ @dataclass -class _VersionInfo(object): +class _VersionInfo: participant_id: ParticipantID version: str query: Query @@ -29,7 +28,7 @@ def __init__( test_env_version_providers: VersionProvidersResource, prod_env_version_providers: VersionProvidersResource, ): - super(EvaluateSystemVersions, self).__init__() + super().__init__() self._test_env_version_providers = test_env_version_providers.version_providers self._prod_env_version_providers = prod_env_version_providers.version_providers self._system_identity = system_identity.system_identity @@ -47,9 +46,9 @@ def run(self, context: ExecutionContext): def _get_versions( self, - ) -> Tuple[Dict[ParticipantID, _VersionInfo], Dict[ParticipantID, _VersionInfo]]: - test_env_versions: Dict[ParticipantID, _VersionInfo] = {} - prod_env_versions: Dict[ParticipantID, _VersionInfo] = {} + ) -> tuple[dict[ParticipantID, _VersionInfo], dict[ParticipantID, _VersionInfo]]: + test_env_versions: dict[ParticipantID, _VersionInfo] = {} + prod_env_versions: dict[ParticipantID, _VersionInfo] = {} for test_step, version_providers, env_versions in ( ( @@ -91,8 +90,8 @@ def _get_versions( def _evaluate_versions( self, - test_env_versions: Dict[ParticipantID, _VersionInfo], - prod_env_versions: Dict[ParticipantID, _VersionInfo], + test_env_versions: dict[ParticipantID, _VersionInfo], + prod_env_versions: dict[ParticipantID, _VersionInfo], ): self.begin_test_step("Evaluate current system versions") @@ -167,7 +166,7 @@ def _evaluate_versions( def _evaluate_consistency( self, context: ExecutionContext, - test_env_versions: Dict[ParticipantID, _VersionInfo], + test_env_versions: dict[ParticipantID, _VersionInfo], ): self.begin_test_step("Evaluate system version consistency") for q in context.sibling_queries(): diff --git a/monitoring/uss_qualifier/scenarios/definitions.py b/monitoring/uss_qualifier/scenarios/definitions.py index 339bc278ee..6dc3473d1b 100644 --- a/monitoring/uss_qualifier/scenarios/definitions.py +++ b/monitoring/uss_qualifier/scenarios/definitions.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - from implicitdict import ImplicitDict from monitoring.uss_qualifier.resources.definitions import ResourceID @@ -12,13 +10,13 @@ class TestScenarioDeclaration(ImplicitDict): scenario_type: TestScenarioTypeName """Type of test scenario.""" - resources: Optional[Dict[ResourceID, ResourceID]] + resources: dict[ResourceID, ResourceID] | None """Mapping of the ID a resource in the test scenario -> the ID a resource is known by in the parent test suite. The additional argument to concrete test scenario constructor is supplied by the parent suite resource . """ def __init__(self, *args, **kwargs): - super(TestScenarioDeclaration, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if "resources" not in self: self.resources = {} diff --git a/monitoring/uss_qualifier/scenarios/documentation/autoformat.py b/monitoring/uss_qualifier/scenarios/documentation/autoformat.py index 605b2eb39e..a8061fdaf6 100644 --- a/monitoring/uss_qualifier/scenarios/documentation/autoformat.py +++ b/monitoring/uss_qualifier/scenarios/documentation/autoformat.py @@ -1,5 +1,5 @@ import os -from typing import Dict, Iterable, List +from collections.abc import Iterable import marko.block import marko.element @@ -17,14 +17,14 @@ def format_scenario_documentation( test_scenarios: Iterable[TestScenarioType], -) -> Dict[str, str]: +) -> dict[str, str]: """Get new documentation content after autoformatting Scenario docs. Returns: Mapping from .md filename to content that file should contain (which is different from what it currently contains). """ - new_versions: Dict[str, str] = {} + new_versions: dict[str, str] = {} to_check = [] for test_scenario in test_scenarios: doc_filename = get_documentation_filename(test_scenario) @@ -41,7 +41,7 @@ def format_scenario_documentation( checked.add(doc_filename) # Load the .md file if it exists - with open(doc_filename, "r") as f: + with open(doc_filename) as f: original = f.read() md = marko.Markdown(renderer=MarkdownRenderer) doc = md.parse(original) @@ -113,7 +113,7 @@ def _add_requirement_links(parent: marko.element.Element, doc_path: str) -> None def _enumerate_linked_test_steps( parent: marko.element.Element, doc_path: str -) -> List[str]: +) -> list[str]: linked_test_steps = [] if hasattr(parent, "children") and not isinstance(parent.children, str): for i, child in enumerate(parent.children): diff --git a/monitoring/uss_qualifier/scenarios/documentation/definitions.py b/monitoring/uss_qualifier/scenarios/documentation/definitions.py index b69a555992..799f5bea9c 100644 --- a/monitoring/uss_qualifier/scenarios/documentation/definitions.py +++ b/monitoring/uss_qualifier/scenarios/documentation/definitions.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from implicitdict import ImplicitDict from monitoring.uss_qualifier.common_data_definitions import Severity @@ -8,24 +6,24 @@ class TestCheckDocumentation(ImplicitDict): name: str - url: Optional[str] = None - applicable_requirements: List[RequirementID] + url: str | None = None + applicable_requirements: list[RequirementID] has_todo: bool - severity: Optional[Severity] = None + severity: Severity | None = None class TestStepDocumentation(ImplicitDict): name: str - url: Optional[str] = None - checks: List[TestCheckDocumentation] + url: str | None = None + checks: list[TestCheckDocumentation] class TestCaseDocumentation(ImplicitDict): name: str - url: Optional[str] = None - steps: List[TestStepDocumentation] + url: str | None = None + steps: list[TestStepDocumentation] - def get_step_by_name(self, step_name: str) -> Optional[TestStepDocumentation]: + def get_step_by_name(self, step_name: str) -> TestStepDocumentation | None: for step in self.steps: if step.name == step_name: return step @@ -34,13 +32,13 @@ def get_step_by_name(self, step_name: str) -> Optional[TestStepDocumentation]: class TestScenarioDocumentation(ImplicitDict): name: str - url: Optional[str] = None + url: str | None = None local_path: str - resources: Optional[List[str]] - cases: List[TestCaseDocumentation] - cleanup: Optional[TestStepDocumentation] + resources: list[str] | None + cases: list[TestCaseDocumentation] + cleanup: TestStepDocumentation | None - def get_case_by_name(self, case_name: str) -> Optional[TestCaseDocumentation]: + def get_case_by_name(self, case_name: str) -> TestCaseDocumentation | None: for case in self.cases: if case.name == case_name: return case diff --git a/monitoring/uss_qualifier/scenarios/documentation/parsing.py b/monitoring/uss_qualifier/scenarios/documentation/parsing.py index fb0c67693b..4bfe83fdbf 100644 --- a/monitoring/uss_qualifier/scenarios/documentation/parsing.py +++ b/monitoring/uss_qualifier/scenarios/documentation/parsing.py @@ -1,6 +1,6 @@ import inspect import os -from typing import Any, Dict, List, Optional, Type +from typing import Any import marko import marko.element @@ -29,7 +29,7 @@ TEST_CHECK_SUFFIX = " check" -_test_step_cache: Dict[str, TestStepDocumentation] = {} +_test_step_cache: dict[str, TestStepDocumentation] = {} def _length_of_section(values, start_of_section: int) -> int: @@ -44,7 +44,7 @@ def _length_of_section(values, start_of_section: int) -> int: return c - start_of_section - 1 -def _requirements_in(values) -> List[RequirementID]: +def _requirements_in(values) -> list[RequirementID]: reqs = [] for v in values: if isinstance(v, marko.inline.StrongEmphasis): @@ -55,7 +55,7 @@ def _requirements_in(values) -> List[RequirementID]: def _parse_test_check( - values, doc_filename: str, anchors: Dict[Any, str] + values, doc_filename: str, anchors: dict[Any, str] ) -> TestCheckDocumentation: name = text_of(values[0])[0 : -len(TEST_CHECK_SUFFIX)] url = repo_url_of(doc_filename + anchors[values[0]]) @@ -94,7 +94,7 @@ def _get_linked_test_step_fragment( raise ValueError( f'Test step fragment document "{doc_filename}" linked from "{origin_filename}" does not exist at "{absolute_path}"' ) - with open(absolute_path, "r") as f: + with open(absolute_path) as f: doc = marko.parse(f.read()) if ( @@ -116,14 +116,14 @@ def _get_linked_test_step_fragment( def _parse_test_step( - values, doc_filename: str, anchors: Dict[Any, str] + values, doc_filename: str, anchors: dict[Any, str] ) -> TestStepDocumentation: name = text_of(values[0]) if name.lower().endswith(TEST_STEP_SUFFIX): name = name[0 : -len(TEST_STEP_SUFFIX)] url = repo_url_of(doc_filename + anchors[values[0]]) - checks: List[TestCheckDocumentation] = [] + checks: list[TestCheckDocumentation] = [] if values[0].children and isinstance( values[0].children[0], marko.block.inline.Link ): @@ -159,13 +159,13 @@ def _parse_test_step( def _parse_test_case( - values, doc_filename: str, anchors: Dict[Any, str] + values, doc_filename: str, anchors: dict[Any, str] ) -> TestCaseDocumentation: name = text_of(values[0])[0 : -len(TEST_CASE_SUFFIX)] url = repo_url_of(doc_filename + anchors[values[0]]) - steps: List[TestStepDocumentation] = [] + steps: list[TestStepDocumentation] = [] c = 1 while c < len(values): if isinstance(values[c], marko.block.Heading): @@ -183,9 +183,9 @@ def _parse_test_case( return TestCaseDocumentation(name=name, steps=steps, url=url) -def _parse_resources(values) -> List[str]: +def _parse_resources(values) -> list[str]: resource_level = values[0].level + 1 - resources: List[str] = [] + resources: list[str] = [] c = 1 while c < len(values): if ( @@ -198,13 +198,11 @@ def _parse_resources(values) -> List[str]: return resources -def get_documentation_filename(scenario: Type) -> str: +def get_documentation_filename(scenario: type) -> str: return os.path.splitext(inspect.getfile(scenario))[0] + ".md" -def _get_anchors( - value, header_counts: Optional[Dict[str, int]] = None -) -> Dict[Any, str]: +def _get_anchors(value, header_counts: dict[str, int] | None = None) -> dict[Any, str]: if header_counts is None: header_counts = {} anchors = {} @@ -231,16 +229,14 @@ def _get_anchors( return anchors -def _parse_documentation(scenario: Type) -> TestScenarioDocumentation: +def _parse_documentation(scenario: type) -> TestScenarioDocumentation: # Load the .md file matching the Python file where this scenario type is defined doc_filename = get_documentation_filename(scenario) if not os.path.exists(doc_filename): raise ValueError( - "Test scenario `{}` does not have the required documentation file `{}`".format( - fullname(scenario), doc_filename - ) + f"Test scenario `{fullname(scenario)}` does not have the required documentation file `{doc_filename}`" ) - with open(doc_filename, "r") as f: + with open(doc_filename) as f: doc = marko.parse(f.read()) url = repo_url_of(doc_filename) anchors = _get_anchors(doc) @@ -252,14 +248,12 @@ def _parse_documentation(scenario: Type) -> TestScenarioDocumentation: or not text_of(doc.children[0]).lower().endswith(TEST_SCENARIO_SUFFIX) ): raise ValueError( - 'The first line of {} must be a level-1 heading with the name of the scenario + "{}" (e.g., "# ASTM NetRID nominal behavior{}")'.format( - doc_filename, TEST_SCENARIO_SUFFIX, TEST_SCENARIO_SUFFIX - ) + f'The first line of {doc_filename} must be a level-1 heading with the name of the scenario + "{TEST_SCENARIO_SUFFIX}" (e.g., "# ASTM NetRID nominal behavior{TEST_SCENARIO_SUFFIX}")' ) scenario_name = text_of(doc.children[0])[0 : -len(TEST_SCENARIO_SUFFIX)] # Step through the document to extract important structured components - test_cases: List[TestCaseDocumentation] = [] + test_cases: list[TestCaseDocumentation] = [] resources = None cleanup = None c = 1 @@ -313,7 +307,7 @@ def _parse_documentation(scenario: Type) -> TestScenarioDocumentation: return TestScenarioDocumentation(**kwargs) -def get_documentation(scenario: Type) -> TestScenarioDocumentation: +def get_documentation(scenario: type) -> TestScenarioDocumentation: DOC_CACHE_ATTRIBUTE = f"_md_documentation_{scenario.__name__}" if not hasattr(scenario, DOC_CACHE_ATTRIBUTE): setattr(scenario, DOC_CACHE_ATTRIBUTE, _parse_documentation(scenario)) diff --git a/monitoring/uss_qualifier/scenarios/documentation/requirements.py b/monitoring/uss_qualifier/scenarios/documentation/requirements.py index fc92891367..7744777dbb 100644 --- a/monitoring/uss_qualifier/scenarios/documentation/requirements.py +++ b/monitoring/uss_qualifier/scenarios/documentation/requirements.py @@ -1,5 +1,3 @@ -from typing import Dict, List, Union - from implicitdict import ImplicitDict from monitoring.monitorlib.dicts import JSONPath @@ -29,10 +27,10 @@ class ParticipantRequirementPerformance(ImplicitDict): - successes: List[JSONPath] + successes: list[JSONPath] """List of passed checks involving the requirement""" - failures: List[JSONPath] + failures: list[JSONPath] """List of failed checks involving the requirement""" @@ -40,17 +38,17 @@ class TestedRequirement(ImplicitDict): requirement_id: RequirementID """Identity of the requirement""" - participant_performance: Dict[ParticipantID, ParticipantRequirementPerformance] + participant_performance: dict[ParticipantID, ParticipantRequirementPerformance] """The performance of each involved participant on the requirement""" def _add_check( - check: Union[PassedCheck, FailedCheck], + check: PassedCheck | FailedCheck, path: JSONPath, scenario_docs: TestScenarioDocumentation, case_docs: TestCaseDocumentation, step_docs: TestStepDocumentation, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ): if not check.requirements: # Generate an implied requirement ID @@ -88,7 +86,7 @@ def _evaluate_requirements_in_step( scenario_docs: TestScenarioDocumentation, case_docs: TestCaseDocumentation, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: step_docs = case_docs.get_step_by_name(report.name) for i, check in enumerate(report.passed_checks): @@ -115,7 +113,7 @@ def _evaluate_requirements_in_case( report: TestCaseReport, scenario_docs: TestScenarioDocumentation, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: case_docs = scenario_docs.get_case_by_name(report.name) for i, step in enumerate(report.steps): @@ -127,7 +125,7 @@ def _evaluate_requirements_in_case( def _evaluate_requirements_in_scenario( report: TestScenarioReport, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: scenario_docs = get_documentation_by_name(report.scenario_type) for i, case in enumerate(report.cases): @@ -139,7 +137,7 @@ def _evaluate_requirements_in_scenario( def _evaluate_requirements_in_action( report: TestSuiteActionReport, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: if "test_suite" in report: _evaluate_requirements_in_suite( @@ -160,7 +158,7 @@ def _evaluate_requirements_in_action( def _evaluate_requirements_in_generator( report: ActionGeneratorReport, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: for i, action in enumerate(report.actions): _evaluate_requirements_in_action(action, path + f".action[{i}]", requirements) @@ -169,13 +167,13 @@ def _evaluate_requirements_in_generator( def _evaluate_requirements_in_suite( report: TestSuiteReport, path: JSONPath, - requirements: Dict[RequirementID, TestedRequirement], + requirements: dict[RequirementID, TestedRequirement], ) -> None: for i, action in enumerate(report.actions): _evaluate_requirements_in_action(action, path + f".action[{i}]", requirements) -def evaluate_requirements(report: TestRunReport) -> List[TestedRequirement]: +def evaluate_requirements(report: TestRunReport) -> list[TestedRequirement]: import_submodules(scenarios_module) reqs = {} _evaluate_requirements_in_action(report.report, "$.report", reqs) diff --git a/monitoring/uss_qualifier/scenarios/documentation/validation.py b/monitoring/uss_qualifier/scenarios/documentation/validation.py index bba331b011..ed1c99732e 100644 --- a/monitoring/uss_qualifier/scenarios/documentation/validation.py +++ b/monitoring/uss_qualifier/scenarios/documentation/validation.py @@ -1,5 +1,4 @@ import inspect -from typing import List from monitoring.monitorlib.inspection import fullname from monitoring.uss_qualifier.requirements.documentation import get_requirement @@ -13,7 +12,7 @@ from monitoring.uss_qualifier.scenarios.scenario import TestScenarioType -def validate(test_scenarios: List[TestScenarioType]): +def validate(test_scenarios: list[TestScenarioType]): for test_scenario in test_scenarios: # Verify that documentation parses docs = get_documentation(test_scenario) diff --git a/monitoring/uss_qualifier/scenarios/eurocae/ed269/source_data_model.py b/monitoring/uss_qualifier/scenarios/eurocae/ed269/source_data_model.py index f1abcb0870..8e8af67908 100644 --- a/monitoring/uss_qualifier/scenarios/eurocae/ed269/source_data_model.py +++ b/monitoring/uss_qualifier/scenarios/eurocae/ed269/source_data_model.py @@ -1,5 +1,4 @@ import json -from typing import List from implicitdict import ImplicitDict, StringBasedDateTime from uas_standards.eurocae_ed269 import UASZoneVersion @@ -15,7 +14,7 @@ class ED269SchemaFile(ImplicitDict): formatVersion: str createdAt: StringBasedDateTime - UASZoneList: List[UASZoneVersion] + UASZoneList: list[UASZoneVersion] class SourceDataModelValidation(TestScenario): diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/prep_planners.py b/monitoring/uss_qualifier/scenarios/flight_planning/prep_planners.py index 5b33edc96f..91306c58e2 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/prep_planners.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/prep_planners.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import Dict, List, Optional import arrow @@ -22,17 +21,17 @@ class PrepareFlightPlannersScenario(TestScenario): - areas: List[Volume4D] - flight_planners: Dict[ParticipantID, FlightPlannerClient] + areas: list[Volume4D] + flight_planners: dict[ParticipantID, FlightPlannerClient] def __init__( self, flight_planners: FlightPlannersResource, flight_intents: FlightIntentsResource, - mock_uss: Optional[MockUSSResource] = None, - flight_intents2: Optional[FlightIntentsResource] = None, - flight_intents3: Optional[FlightIntentsResource] = None, - flight_intents4: Optional[FlightIntentsResource] = None, + mock_uss: MockUSSResource | None = None, + flight_intents2: FlightIntentsResource | None = None, + flight_intents3: FlightIntentsResource | None = None, + flight_intents4: FlightIntentsResource | None = None, ): super().__init__() now = Time(arrow.utcnow().datetime) diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py b/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py index 5f4ed20d68..b264cf8211 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py @@ -1,5 +1,3 @@ -from typing import Optional - from uas_standards.interuss.automated_testing.flight_planning.v1.api import ( BasicFlightPlanInformationUasState, BasicFlightPlanInformationUsageState, @@ -23,7 +21,7 @@ def plan_priority_conflict_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to plan a flight intent that should result in a conflict with a higher priority flight intent. @@ -58,7 +56,7 @@ def modify_planned_priority_conflict_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, flight_id: str, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to modify a planned flight intent that should result in a conflict with a higher priority flight intent. @@ -96,8 +94,8 @@ def activate_priority_conflict_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - flight_id: Optional[str] = None, - additional_fields: Optional[dict] = None, + flight_id: str | None = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to activate a flight intent that should result in a conflict with a higher priority flight intent. @@ -140,7 +138,7 @@ def modify_activated_priority_conflict_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, flight_id: str, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to modify an activated flight intent that should result in a conflict with a higher priority flight intent. @@ -178,7 +176,7 @@ def plan_conflict_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to plan a flight intent that should result in a non-permitted conflict with an equal priority flight intent. @@ -212,7 +210,7 @@ def modify_planned_conflict_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, flight_id: str, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to modify a planned flight intent that should result in a non-permitted conflict with an equal priority flight intent. @@ -250,8 +248,8 @@ def activate_conflict_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - flight_id: Optional[str] = None, - additional_fields: Optional[dict] = None, + flight_id: str | None = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to activate a flight intent that should result in a non-permitted conflict with an equal priority flight intent. @@ -294,7 +292,7 @@ def modify_activated_conflict_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, flight_id: str, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Attempt to modify an activated flight intent that should result in a non-permitted conflict with an equal priority flight intent. diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py index 5999028c2f..ac82226140 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py @@ -1,5 +1,5 @@ import inspect -from typing import Dict, Iterable, Optional, Set, Tuple +from collections.abc import Iterable import arrow from uas_standards.interuss.automated_testing.flight_planning.v1.api import ( @@ -46,9 +46,9 @@ def plan_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, nearby_potential_conflict: bool = False, -) -> Tuple[PlanningActivityResponse, Optional[str]]: +) -> tuple[PlanningActivityResponse, str | None]: """Plan a flight intent that should result in success. This function implements the test step fragment described in @@ -88,7 +88,7 @@ def modify_planned_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, flight_id: str, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Modify a planned flight intent that should result in success. @@ -122,7 +122,7 @@ def modify_activated_flight( flight_info: FlightInfo, flight_id: str, preexisting_conflict: bool = False, - additional_fields: Optional[dict] = None, + additional_fields: dict | None = None, ) -> PlanningActivityResponse: """Modify an activated flight intent that should result in success. @@ -190,9 +190,9 @@ def activate_flight( scenario: TestScenarioType, flight_planner: FlightPlannerClient, flight_info: FlightInfo, - flight_id: Optional[str] = None, - additional_fields: Optional[dict] = None, -) -> Tuple[PlanningActivityResponse, Optional[str]]: + flight_id: str | None = None, + additional_fields: dict | None = None, +) -> tuple[PlanningActivityResponse, str | None]: """Activate a flight intent that should result in success. This function implements the test step fragment described in @@ -217,15 +217,15 @@ def activate_flight( def submit_flight( scenario: TestScenarioType, success_check: str, - expected_results: Set[Tuple[PlanningActivityResult, FlightPlanStatus]], - failed_checks: Dict[PlanningActivityResult, str], + expected_results: set[tuple[PlanningActivityResult, FlightPlanStatus]], + failed_checks: dict[PlanningActivityResult, str], flight_planner: FlightPlannerClient, flight_info: FlightInfo, - flight_id: Optional[str] = None, - additional_fields: Optional[dict] = None, + flight_id: str | None = None, + additional_fields: dict | None = None, skip_if_not_supported: bool = False, may_end_in_past: bool = False, -) -> Tuple[PlanningActivityResponse, Optional[str]]: +) -> tuple[PlanningActivityResponse, str | None]: """Submit a flight intent with an expected result. A check fail is considered by default of high severity and as such will raise an ScenarioCannotContinueError. The severity of each failed check may be overridden if needed. @@ -302,9 +302,9 @@ def submit_flight( def request_flight( flight_planner: FlightPlannerClient, flight_info: FlightInfo, - flight_id: Optional[str], - additional_fields: Optional[dict] = None, -) -> Tuple[PlanningActivityResponse, Query, str]: + flight_id: str | None, + additional_fields: dict | None = None, +) -> tuple[PlanningActivityResponse, Query, str]: """ Uses FlightPlannerClient to plan the flight @@ -334,7 +334,7 @@ def request_flight( def cleanup_flight( flight_planner: FlightPlannerClient, flight_id: str -) -> Tuple[PlanningActivityResponse, Query]: +) -> tuple[PlanningActivityResponse, Query]: try: resp = flight_planner.try_end_flight(flight_id, ExecutionStyle.IfAllowed) except PlanningActivityError as e: diff --git a/monitoring/uss_qualifier/scenarios/interuss/flight_authorization/general_flight_authorization.py b/monitoring/uss_qualifier/scenarios/interuss/flight_authorization/general_flight_authorization.py index 79a306a530..a757f7b73a 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/flight_authorization/general_flight_authorization.py +++ b/monitoring/uss_qualifier/scenarios/interuss/flight_authorization/general_flight_authorization.py @@ -1,5 +1,3 @@ -from typing import Dict - import arrow from monitoring.monitorlib.clients.flight_planning.client import ( @@ -78,7 +76,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() - def _plan_flights(self, times: Dict[TimeDuringTest, Time]): + def _plan_flights(self, times: dict[TimeDuringTest, Time]): for row in self.table.rows: # Collect checks applicable to this row/test step checks = [ diff --git a/monitoring/uss_qualifier/scenarios/interuss/geospatial_map/geospatial_feature_comprehension.py b/monitoring/uss_qualifier/scenarios/interuss/geospatial_map/geospatial_feature_comprehension.py index b6dce6d252..65509d2c7f 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/geospatial_map/geospatial_feature_comprehension.py +++ b/monitoring/uss_qualifier/scenarios/interuss/geospatial_map/geospatial_feature_comprehension.py @@ -1,5 +1,3 @@ -from typing import Dict - import arrow from monitoring.monitorlib.clients.geospatial_info.client import ( @@ -70,7 +68,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() - def _map_query(self, times: Dict[TimeDuringTest, Time]): + def _map_query(self, times: dict[TimeDuringTest, Time]): query_check = [ c for c in self._current_case.steps[0].checks diff --git a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/configure_locality.py b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/configure_locality.py index 3c3177b039..c46f830cb6 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/configure_locality.py +++ b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/configure_locality.py @@ -1,5 +1,3 @@ -from typing import List - from monitoring.monitorlib.locality import LocalityCode from monitoring.uss_qualifier.resources.interuss.mock_uss.client import ( MockUSSClient, @@ -17,14 +15,14 @@ class ConfigureLocality(TestScenario): - mock_uss_instances: List[MockUSSClient] + mock_uss_instances: list[MockUSSClient] locality_code: LocalityCode - to_unconfigure: List[MockUSSLocalityConfiguration] + to_unconfigure: list[MockUSSLocalityConfiguration] def __init__( self, mock_uss_instances: MockUSSsResource, locality: LocalityResource ): - super(ConfigureLocality, self).__init__() + super().__init__() self.mock_uss_instances = mock_uss_instances.mock_uss_instances self.locality_code = locality.locality_code self.to_unconfigure = [] diff --git a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/test_steps.py b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/test_steps.py index d793f2154d..b679fa4e77 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/test_steps.py @@ -1,5 +1,5 @@ import re -from typing import Callable, Iterable, List, Tuple +from collections.abc import Callable, Iterable from implicitdict import StringBasedDateTime from uas_standards.astm.f3548.v21 import api @@ -19,7 +19,7 @@ def get_mock_uss_interactions( mock_uss: MockUSSClient, since: StringBasedDateTime, *is_applicable: Callable[[Interaction], bool], -) -> Tuple[List[Interaction], Query]: +) -> tuple[list[Interaction], Query]: """Retrieves mock_uss interactions given specific criteria. Implements test step fragment in `get_mock_uss_interactions.md`.""" @@ -41,8 +41,8 @@ def get_mock_uss_interactions( def filter_interactions( - interactions: List[Interaction], filters: Iterable[Callable[[Interaction], bool]] -) -> List[Interaction]: + interactions: list[Interaction], filters: Iterable[Callable[[Interaction], bool]] +) -> list[Interaction]: return list(filter(lambda x: all(f(x) for f in filters), interactions)) diff --git a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/unconfigure_locality.py b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/unconfigure_locality.py index 651122f1c4..fdd67c3c99 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/mock_uss/unconfigure_locality.py +++ b/monitoring/uss_qualifier/scenarios/interuss/mock_uss/unconfigure_locality.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from typing import List from monitoring.monitorlib.locality import LocalityCode from monitoring.uss_qualifier.resources.interuss.mock_uss.client import MockUSSClient @@ -8,12 +7,12 @@ @dataclass -class MockUSSLocalityConfiguration(object): +class MockUSSLocalityConfiguration: client: MockUSSClient locality_code: LocalityCode -unconfigure_stack: List[List[MockUSSLocalityConfiguration]] = [] +unconfigure_stack: list[list[MockUSSLocalityConfiguration]] = [] """The stack of mock_uss locality configurations that have been performed by configure_locality. UnconfigureLocality will reset localities according to the most recent stack addition.""" diff --git a/monitoring/uss_qualifier/scenarios/interuss/ovn_request/dss_ovn_request.py b/monitoring/uss_qualifier/scenarios/interuss/ovn_request/dss_ovn_request.py index 62c852f80b..cc77b9443a 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/ovn_request/dss_ovn_request.py +++ b/monitoring/uss_qualifier/scenarios/interuss/ovn_request/dss_ovn_request.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import List from uas_standards.astm.f3548.v21.api import ( OperationalIntentReference, @@ -109,7 +108,7 @@ def run(self, context: ExecutionContext): self.end_test_scenario() def _create_oir( - self, extents: List[Volume4D], req_ovn_suffix: str + self, extents: list[Volume4D], req_ovn_suffix: str ) -> OperationalIntentReference: with self.check( "Create operational intent reference query succeeds", @@ -136,7 +135,7 @@ def _create_oir( return oir - def _activate_oir(self, extents: List[Volume4D], ovn: str, req_ovn_suffix: str): + def _activate_oir(self, extents: list[Volume4D], ovn: str, req_ovn_suffix: str): with self.check( "Mutate operational intent reference query succeeds", [self._dss.participant_id], @@ -162,7 +161,7 @@ def _activate_oir(self, extents: List[Volume4D], ovn: str, req_ovn_suffix: str): return oir - def _create_invalid_oir_attempt(self, extents: List[Volume4D], req_ovn_suffix: str): + def _create_invalid_oir_attempt(self, extents: list[Volume4D], req_ovn_suffix: str): with self.check( "Attempt to create OIR with invalid requested OVN suffix query rejected", [self._dss.participant_id], diff --git a/monitoring/uss_qualifier/scenarios/interuss/unit_test.py b/monitoring/uss_qualifier/scenarios/interuss/unit_test.py index 33c5ed65f1..30aac26e96 100644 --- a/monitoring/uss_qualifier/scenarios/interuss/unit_test.py +++ b/monitoring/uss_qualifier/scenarios/interuss/unit_test.py @@ -1,4 +1,4 @@ -from typing import Callable +from collections.abc import Callable from monitoring.uss_qualifier.scenarios.definitions import TestScenarioDeclaration from monitoring.uss_qualifier.scenarios.scenario import GenericTestScenario diff --git a/monitoring/uss_qualifier/scenarios/scenario.py b/monitoring/uss_qualifier/scenarios/scenario.py index a479785a03..76016f1180 100644 --- a/monitoring/uss_qualifier/scenarios/scenario.py +++ b/monitoring/uss_qualifier/scenarios/scenario.py @@ -1,9 +1,10 @@ import inspect import traceback from abc import ABC, abstractmethod +from collections.abc import Callable from datetime import UTC, datetime from enum import Enum -from typing import Callable, Dict, List, Optional, Set, Type, TypeVar, Union +from typing import TypeVar import arrow from implicitdict import StringBasedDateTime @@ -59,12 +60,12 @@ class ScenarioCannotContinueError(Exception): def __init__(self, msg): - super(ScenarioCannotContinueError, self).__init__(msg) + super().__init__(msg) class TestRunCannotContinueError(Exception): def __init__(self, msg): - super(TestRunCannotContinueError, self).__init__(msg) + super().__init__(msg) class ScenarioPhase(str, Enum): @@ -78,23 +79,23 @@ class ScenarioPhase(str, Enum): Complete = "Complete" -class PendingCheck(object): +class PendingCheck: _phase: ScenarioPhase _documentation: TestCheckDocumentation _step_report: TestStepReport _stop_fast: bool - _on_failed_check: Optional[Callable[[FailedCheck], None]] - _participants: List[ParticipantID] + _on_failed_check: Callable[[FailedCheck], None] | None + _participants: list[ParticipantID] _outcome_recorded: bool = False def __init__( self, phase: ScenarioPhase, documentation: TestCheckDocumentation, - participants: List[ParticipantID], + participants: list[ParticipantID], step_report: TestStepReport, stop_fast: bool, - on_failed_check: Optional[Callable[[FailedCheck], None]], + on_failed_check: Callable[[FailedCheck], None] | None, ): self._phase = phase self._documentation = documentation @@ -114,8 +115,8 @@ def record_failed( self, summary: str, details: str = "", - query_timestamps: Optional[List[datetime]] = None, - additional_data: Optional[dict] = None, + query_timestamps: list[datetime] | None = None, + additional_data: dict | None = None, ) -> None: self._outcome_recorded = True if "severity" in self._documentation and self._documentation.severity: @@ -176,16 +177,14 @@ def skip(self) -> None: self._outcome_recorded = True -def get_scenario_type_by_name(scenario_type_name: TestScenarioTypeName) -> Type: +def get_scenario_type_by_name(scenario_type_name: TestScenarioTypeName) -> type: inspection.import_submodules(scenarios_module) scenario_type = inspection.get_module_object_by_name( parent_module=uss_qualifier_module, object_name=scenario_type_name ) if not issubclass(scenario_type, TestScenario): raise NotImplementedError( - "Scenario type {} is not a subclass of the TestScenario base class".format( - scenario_type.__name__ - ) + f"Scenario type {scenario_type.__name__} is not a subclass of the TestScenario base class" ) return scenario_type @@ -198,17 +197,17 @@ class GenericTestScenario(ABC): declaration: TestScenarioDeclaration documentation: TestScenarioDocumentation - on_failed_check: Optional[Callable[[FailedCheck], None]] = None + on_failed_check: Callable[[FailedCheck], None] | None = None - resource_origins: Dict[ResourceID, str] + resource_origins: dict[ResourceID, str] """Map between local resource name (as defined in test scenario) to where that resource originated.""" _phase: ScenarioPhase = ScenarioPhase.Undefined - _scenario_report: Optional[TestScenarioReport] = None - _current_case: Optional[TestCaseDocumentation] = None - _case_report: Optional[TestCaseReport] = None - _current_step: Optional[TestStepDocumentation] = None - _step_report: Optional[TestStepReport] = None + _scenario_report: TestScenarioReport | None = None + _current_case: TestCaseDocumentation | None = None + _case_report: TestCaseReport | None = None + _current_step: TestStepDocumentation | None = None + _step_report: TestStepReport | None = None _allow_undocumented_checks = False """When this variable is set to True, it allows undocumented checks to be executed by the scenario. This is primarly intended to simplify internal unit testing.""" @@ -223,7 +222,7 @@ def __init__(self): @staticmethod def make_test_scenario( declaration: TestScenarioDeclaration, - resource_pool: Dict[ResourceID, ResourceType], + resource_pool: dict[ResourceID, ResourceType], ) -> "TestScenario": scenario_type = get_scenario_type_by_name(declaration.scenario_type) @@ -272,7 +271,7 @@ def cleanup(self): def me(self) -> str: return inspection.fullname(self.__class__) - def current_step_name(self) -> Optional[str]: + def current_step_name(self) -> str | None: if self._current_step: return self._current_step.name else: @@ -288,7 +287,7 @@ def _make_scenario_report(self) -> None: cases=[], ) - def _expect_phase(self, expected_phase: Union[ScenarioPhase, Set[ScenarioPhase]]): + def _expect_phase(self, expected_phase: ScenarioPhase | set[ScenarioPhase]): if isinstance(expected_phase, ScenarioPhase): expected_phase = {expected_phase} if self._phase not in expected_phase: @@ -394,7 +393,7 @@ def _begin_test_step(self, step: TestStepDocumentation) -> None: self._case_report.steps.append(self._step_report) self._phase = ScenarioPhase.RunningTestStep - def record_queries(self, queries: List[fetch.Query]) -> None: + def record_queries(self, queries: list[fetch.Query]) -> None: for q in queries: self.record_query(q) @@ -436,7 +435,7 @@ def record_query(self, query: fetch.Query) -> None: def check( self, name: str, - participants: Optional[Union[ParticipantID, List[ParticipantID]]] = None, + participants: ParticipantID | list[ParticipantID] | None = None, ) -> PendingCheck: if isinstance(participants, str): participants = [participants] @@ -600,7 +599,7 @@ class TestScenario(GenericTestScenario): pass -def get_scenario_type_name(scenario_type: Type[TestScenario]) -> TestScenarioTypeName: +def get_scenario_type_name(scenario_type: type[TestScenario]) -> TestScenarioTypeName: full_name = fullname(scenario_type) if not issubclass(scenario_type, TestScenario): raise ValueError(f"{full_name} is not a TestScenario") @@ -615,8 +614,8 @@ def get_scenario_type_name(scenario_type: Type[TestScenario]) -> TestScenarioTyp def find_test_scenarios( - module, already_checked: Optional[Set[str]] = None -) -> List[TestScenarioType]: + module, already_checked: set[str] | None = None +) -> list[TestScenarioType]: if already_checked is None: already_checked = set() already_checked.add(module.__name__) diff --git a/monitoring/uss_qualifier/scenarios/scenario_test/utils.py b/monitoring/uss_qualifier/scenarios/scenario_test/utils.py index 01caffe436..59acd1d937 100644 --- a/monitoring/uss_qualifier/scenarios/scenario_test/utils.py +++ b/monitoring/uss_qualifier/scenarios/scenario_test/utils.py @@ -1,6 +1,6 @@ import datetime import importlib -from typing import Callable, Optional, Tuple +from collections.abc import Callable import arrow from implicitdict import StringBasedDateTime @@ -108,11 +108,11 @@ def assert_date_is_close_to_now(d: datetime.datetime): def build_testable_pending_check( - phase: Optional[ScenarioPhase] = None, - severity: Optional[Severity] = None, + phase: ScenarioPhase | None = None, + severity: Severity | None = None, stop_fast: bool = False, - on_failed_check: Optional[Callable[[FailedCheck], None]] = None, -) -> Tuple[PendingCheck, _TestStepReport]: + on_failed_check: Callable[[FailedCheck], None] | None = None, +) -> tuple[PendingCheck, _TestStepReport]: """Return a (PendingCheck, Report) instances with mocked relative objects""" if not phase: diff --git a/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py b/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py index c733e17c72..ad8241c114 100644 --- a/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py +++ b/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py @@ -1,5 +1,3 @@ -from typing import Dict, List - import arrow from monitoring.monitorlib.clients.flight_planning.client import FlightPlannerClient @@ -34,9 +32,9 @@ class Validation(TestScenario): - times: Dict[TimeDuringTest, Time] + times: dict[TimeDuringTest, Time] - invalid_flight_intents: List[FlightInfoTemplate] + invalid_flight_intents: list[FlightInfoTemplate] valid_flight_intent: FlightInfoTemplate ussp: FlightPlannerClient diff --git a/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py b/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py index f6e034f9f2..9ce056a4e6 100644 --- a/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py +++ b/monitoring/uss_qualifier/scenarios/uspace/netrid/msl.py @@ -1,5 +1,3 @@ -from typing import List - import s2sphere from implicitdict import ImplicitDict from uas_standards.interuss.automated_testing.rid.v1.observation import ( @@ -21,7 +19,7 @@ class MSLAltitude(TestScenario): - _ussps: List[ParticipantID] + _ussps: list[ParticipantID] def __init__(self, observers: NetRIDObserversResource): super().__init__() @@ -53,7 +51,7 @@ def run(self, context): self.end_test_scenario() - def _evaluate_msl_altitude(self, queries: List[Query]): + def _evaluate_msl_altitude(self, queries: list[Query]): for query in queries: if ( "query_type" not in query diff --git a/monitoring/uss_qualifier/scenarios/versioning/get_system_versions.py b/monitoring/uss_qualifier/scenarios/versioning/get_system_versions.py index 3afec67def..4ed86f1dd4 100644 --- a/monitoring/uss_qualifier/scenarios/versioning/get_system_versions.py +++ b/monitoring/uss_qualifier/scenarios/versioning/get_system_versions.py @@ -13,7 +13,7 @@ def __init__( version_providers: VersionProvidersResource, system_identity: SystemIdentityResource, ): - super(GetSystemVersions, self).__init__() + super().__init__() self._version_providers = version_providers.version_providers self._system_identity = system_identity.system_identity diff --git a/monitoring/uss_qualifier/scripts/report_analyzer.py b/monitoring/uss_qualifier/scripts/report_analyzer.py index 359ce75454..ec64cb3de9 100644 --- a/monitoring/uss_qualifier/scripts/report_analyzer.py +++ b/monitoring/uss_qualifier/scripts/report_analyzer.py @@ -11,7 +11,7 @@ def parse_report(path: str) -> TestRunReport: - with open(path, "r") as f: + with open(path) as f: report = json.load(f) return ImplicitDict.parse(report, TestRunReport) diff --git a/monitoring/uss_qualifier/signatures.py b/monitoring/uss_qualifier/signatures.py index 492eb78bdb..eac78e3518 100644 --- a/monitoring/uss_qualifier/signatures.py +++ b/monitoring/uss_qualifier/signatures.py @@ -1,6 +1,6 @@ import hashlib import json -from typing import Any, Union +from typing import Any def compute_baseline_signature( @@ -42,12 +42,12 @@ def _with_integer_zeros(obj: Any) -> Any: elif isinstance(obj, list): return [_with_integer_zeros(v) for v in obj] elif isinstance(obj, float) and obj == 0: - return int(0) + return 0 else: return obj -def compute_signature(obj: Union[dict, list, str]) -> str: +def compute_signature(obj: dict | list | str) -> str: """Compute a hash/signature of the content of the dict object.""" if isinstance(obj, str): sig = hashlib.sha256() diff --git a/monitoring/uss_qualifier/suites/definitions.py b/monitoring/uss_qualifier/suites/definitions.py index 3ad8e9f250..54ceef60d7 100644 --- a/monitoring/uss_qualifier/suites/definitions.py +++ b/monitoring/uss_qualifier/suites/definitions.py @@ -1,7 +1,6 @@ from __future__ import annotations from enum import Enum -from typing import Dict, List, Optional from implicitdict import ImplicitDict @@ -23,20 +22,20 @@ class TestSuiteDeclaration(ImplicitDict): - suite_type: Optional[TestSuiteTypeName] + suite_type: TestSuiteTypeName | None """Type/location of test suite. Usually expressed as the file name of the suite definition (without extension) qualified relative to the `uss_qualifier` folder""" - suite_definition: Optional[TestSuiteDefinition] + suite_definition: TestSuiteDefinition | None """Definition of test suite internal to the configuration -- specified instead of `suite_type`.""" - resources: Optional[Dict[ResourceID, ResourceID]] + resources: dict[ResourceID, ResourceID] | None """Mapping of the ID a resource will be known by in the child test suite -> the ID a resource is known by in the parent test suite. The child suite resource is supplied by the parent suite resource . """ def __init__(self, *args, **kwargs): - super(TestSuiteDeclaration, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if ( "suite_type" in self and self.suite_type @@ -87,13 +86,13 @@ class TestSuiteActionDeclaration(ImplicitDict): Exactly one of `test_scenario`, `test_suite`, or `action_generator` must be specified. """ - test_scenario: Optional[TestScenarioDeclaration] + test_scenario: TestScenarioDeclaration | None """If this field is populated, declaration of the test scenario to run""" - test_suite: Optional[TestSuiteDeclaration] + test_suite: TestSuiteDeclaration | None """If this field is populated, declaration of the test suite to run""" - action_generator: Optional[ActionGeneratorDefinition] + action_generator: ActionGeneratorDefinition | None """If this field is populated, declaration of a generator that will produce 0 or more test suite actions""" on_failure: ReactionToFailure = ReactionToFailure.Continue @@ -105,7 +104,7 @@ def get_action_type(self) -> ActionType: ActionType.raise_invalid_action_declaration() return ActionType(matches[0]) - def get_resource_links(self) -> Dict[ResourceID, ResourceID]: + def get_resource_links(self) -> dict[ResourceID, ResourceID]: action_type = self.get_action_type() if action_type == ActionType.TestScenario: return self.test_scenario.resources @@ -138,16 +137,16 @@ class TestSuiteDefinition(ImplicitDict): name: str """Name of the test suite""" - resources: Dict[ResourceID, ResourceTypeNameSpecifyingOptional] + resources: dict[ResourceID, ResourceTypeNameSpecifyingOptional] """Enumeration of the resources used by this test suite""" - local_resources: Optional[Dict[ResourceID, ResourceDeclaration]] + local_resources: dict[ResourceID, ResourceDeclaration] | None """Declarations of resources originating in this test suite. If a resource is defined in both `resources` and `local_resources`, the resource in `local_resources` will be ignored (`resources` overrides `local_resources`).""" - actions: List[TestSuiteActionDeclaration] + actions: list[TestSuiteActionDeclaration] """The actions to take when running the test suite. Components will be executed in order.""" - participant_verifiable_capabilities: Optional[List[ParticipantCapabilityDefinition]] + participant_verifiable_capabilities: list[ParticipantCapabilityDefinition] | None """Definitions of capabilities verified by this test suite for individual participants.""" @staticmethod diff --git a/monitoring/uss_qualifier/suites/documentation/documentation.py b/monitoring/uss_qualifier/suites/documentation/documentation.py index 21d94fb7f0..fd2be7112a 100644 --- a/monitoring/uss_qualifier/suites/documentation/documentation.py +++ b/monitoring/uss_qualifier/suites/documentation/documentation.py @@ -3,8 +3,8 @@ import glob import inspect import os +from collections.abc import Iterator from dataclasses import dataclass -from typing import Dict, Iterator, List, Optional, Union from implicitdict import ImplicitDict @@ -47,35 +47,33 @@ @dataclass -class TestSuiteRenderContext(object): +class TestSuiteRenderContext: parent_yaml_file: str parent_doc_file: str base_path: str list_index: int indent: int - test_suites: Dict[str, str] + test_suites: dict[str, str] -def find_test_suites(start_path: Optional[str] = None) -> Iterator[str]: +def find_test_suites(start_path: str | None = None) -> Iterator[str]: if start_path is None: start_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - for yaml in glob.glob(os.path.join(start_path, "*.yaml")): - yield yaml + yield from glob.glob(os.path.join(start_path, "*.yaml")) for subfolder in os.listdir(start_path): full_path = os.path.join(start_path, subfolder) if not os.path.isdir(full_path): continue - for suite in find_test_suites(full_path): - yield suite + yield from find_test_suites(full_path) def make_test_suite_documentation( suite_def: TestSuiteDefinition, suite_yaml_file: str, suite_doc_file: str, - parent_suite_doc: Optional[str] = None, -) -> Dict[str, str]: - test_suites: Dict[str, str] = {} + parent_suite_doc: str | None = None, +) -> dict[str, str]: + test_suites: dict[str, str] = {} lines = [] lines.append( @@ -140,7 +138,7 @@ def make_test_suite_documentation( ) lines.append(" ") - req_ids_by_package: Dict[str, List[RequirementID]] = {} + req_ids_by_package: dict[str, list[RequirementID]] = {} for req_id, req in reqs.items(): package = req_ids_by_package.get(req_id.package(), []) if req_id not in package: @@ -199,7 +197,7 @@ def make_test_suite_documentation( def _render_scenario( scenario_type_name: TestScenarioTypeName, context: TestSuiteRenderContext -) -> List[str]: +) -> list[str]: lines = [] scenario_type = get_scenario_type_by_name(scenario_type_name) py_rel_path = os.path.relpath(inspect.getfile(scenario_type), context.base_path) @@ -213,7 +211,7 @@ def _render_scenario( def _render_suite_by_type( suite_type: TestSuiteTypeName, context: TestSuiteRenderContext -) -> List[str]: +) -> list[str]: lines = [] suite_def = ImplicitDict.parse( load_dict_with_references(suite_type), @@ -231,7 +229,7 @@ def _render_suite_by_type( def _render_suite_by_definition( suite_def: TestSuiteDefinition, context: TestSuiteRenderContext -) -> List[str]: +) -> list[str]: doc_path = ( os.path.splitext(context.parent_doc_file)[0] + f"_suite{context.list_index}.md" ) @@ -250,9 +248,9 @@ def _render_suite_by_definition( def _render_action_generator( - generator_def: Union[ActionGeneratorDefinition, PotentialActionGeneratorAction], + generator_def: ActionGeneratorDefinition | PotentialActionGeneratorAction, context: TestSuiteRenderContext, -) -> List[str]: +) -> list[str]: lines = [] action_generator_type = action_generator_type_from_name( generator_def.generator_type @@ -284,9 +282,9 @@ def _render_action_generator( def _render_action( - action: Union[TestSuiteActionDeclaration, PotentialGeneratedAction], + action: TestSuiteActionDeclaration | PotentialGeneratedAction, context: TestSuiteRenderContext, -) -> List[str]: +) -> list[str]: action_type = action.get_action_type() if action_type == ActionType.TestScenario: return _render_scenario(action.test_scenario.scenario_type, context) @@ -311,14 +309,14 @@ def _render_action( @dataclass -class SuiteLocation(object): +class SuiteLocation: scenario: TestScenarioDocumentation check: TestCheckDocumentation @dataclass -class RequirementInSuite(object): - checked_in: List[SuiteLocation] +class RequirementInSuite: + checked_in: list[SuiteLocation] def extend(self, other: RequirementInSuite): self.checked_in.extend(other.checked_in) @@ -326,10 +324,10 @@ def extend(self, other: RequirementInSuite): def _collect_requirements_from_suite_def( suite_def: TestSuiteDefinition, -) -> Dict[RequirementID, RequirementInSuite]: - reqs: Dict[RequirementID, RequirementInSuite] = {} +) -> dict[RequirementID, RequirementInSuite]: + reqs: dict[RequirementID, RequirementInSuite] = {} - def combine(new_reqs: Dict[RequirementID, RequirementInSuite]) -> None: + def combine(new_reqs: dict[RequirementID, RequirementInSuite]) -> None: for req_id, req in new_reqs.items(): if req_id not in reqs: reqs[req_id] = req @@ -342,8 +340,8 @@ def combine(new_reqs: Dict[RequirementID, RequirementInSuite]) -> None: def _collect_requirements_from_action( - action: Union[TestSuiteActionDeclaration, PotentialGeneratedAction], -) -> Dict[RequirementID, RequirementInSuite]: + action: TestSuiteActionDeclaration | PotentialGeneratedAction, +) -> dict[RequirementID, RequirementInSuite]: action_type = action.get_action_type() if action_type == ActionType.TestScenario: return _collect_requirements_from_scenario(action.test_scenario.scenario_type) @@ -375,9 +373,9 @@ def _collect_requirements_from_action( def _collect_requirements_from_scenario( scenario_type: TestScenarioTypeName, -) -> Dict[RequirementID, RequirementInSuite]: +) -> dict[RequirementID, RequirementInSuite]: docs = get_documentation_by_name(scenario_type) - reqs: Dict[RequirementID, RequirementInSuite] = {} + reqs: dict[RequirementID, RequirementInSuite] = {} def add_req(req_id: RequirementID, check: TestCheckDocumentation) -> None: req = reqs.get(req_id, RequirementInSuite(checked_in=[])) @@ -397,13 +395,13 @@ def add_req(req_id: RequirementID, check: TestCheckDocumentation) -> None: def _collect_requirements_from_action_generator( - generator_def: Union[ActionGeneratorDefinition, PotentialActionGeneratorAction], -) -> Dict[RequirementID, RequirementInSuite]: + generator_def: ActionGeneratorDefinition | PotentialActionGeneratorAction, +) -> dict[RequirementID, RequirementInSuite]: potential_actions = list_potential_actions_for_action_generator_definition( generator_def ) - reqs: Dict[RequirementID, RequirementInSuite] = {} + reqs: dict[RequirementID, RequirementInSuite] = {} for potential_action in potential_actions: new_reqs = _collect_requirements_from_action(potential_action) for req_id, req in new_reqs.items(): diff --git a/monitoring/uss_qualifier/suites/documentation/format_documentation.py b/monitoring/uss_qualifier/suites/documentation/format_documentation.py index 5303417fdd..00e7248bc8 100644 --- a/monitoring/uss_qualifier/suites/documentation/format_documentation.py +++ b/monitoring/uss_qualifier/suites/documentation/format_documentation.py @@ -35,7 +35,7 @@ def main(lint: bool) -> int: changes = False for suite_doc_file, suite_doc_content in test_suite_docs.items(): if os.path.exists(suite_doc_file): - with open(suite_doc_file, "r") as f: + with open(suite_doc_file) as f: existing_content = f.read() if existing_content == suite_doc_content: # No changes needed diff --git a/monitoring/uss_qualifier/suites/suite.py b/monitoring/uss_qualifier/suites/suite.py index 7f8ca65b4c..13ef83454a 100644 --- a/monitoring/uss_qualifier/suites/suite.py +++ b/monitoring/uss_qualifier/suites/suite.py @@ -3,9 +3,9 @@ import json import os import re +from collections.abc import Iterator from dataclasses import dataclass from datetime import UTC, datetime -from typing import Dict, Iterator, List, Optional, Type, Union import arrow import yaml @@ -73,16 +73,16 @@ def _print_failed_check(failed_check: FailedCheck) -> None: ) -class TestSuiteAction(object): +class TestSuiteAction: declaration: TestSuiteActionDeclaration - test_scenario: Optional[TestScenario] = None - test_suite: Optional[TestSuite] = None - action_generator: Optional[ActionGeneratorType] = None + test_scenario: TestScenario | None = None + test_suite: TestSuite | None = None + action_generator: ActionGeneratorType | None = None def __init__( self, action: TestSuiteActionDeclaration, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): self.declaration = action resources_for_child = make_child_resources( @@ -206,17 +206,17 @@ def _run_action_generator(self, context: ExecutionContext) -> ActionGeneratorRep return report -class TestSuite(object): +class TestSuite: declaration: TestSuiteDeclaration definition: TestSuiteDefinition documentation_url: str - local_resources: Dict[ResourceID, ResourceType] - actions: List[Union[TestSuiteAction, SkippedActionReport]] + local_resources: dict[ResourceID, ResourceType] + actions: list[TestSuiteAction | SkippedActionReport] def __init__( self, declaration: TestSuiteDeclaration, - resources: Dict[ResourceID, ResourceType], + resources: dict[ResourceID, ResourceType], ): # Determine the suite's documentation URL if "suite_type" in declaration and declaration.suite_type: @@ -276,7 +276,7 @@ def __init__( raise ValueError( f'Test suite "{self.definition.name}" expected resource {resource_id} to be {resource_type}, but instead it was provided {fullname(self.local_resources[resource_id].__class__)}' ) - actions: List[Union[TestSuiteAction, SkippedActionReport]] = [] + actions: list[TestSuiteAction | SkippedActionReport] = [] for a, action_dec in enumerate(self.definition.actions): try: actions.append( @@ -305,9 +305,8 @@ def run(self, context: ExecutionContext) -> TestSuiteReport: capability_evaluations=[], ) - def actions() -> Iterator[Union[TestSuiteAction, SkippedActionReport]]: - for a in self.actions: - yield a + def actions() -> Iterator[TestSuiteAction | SkippedActionReport]: + yield from self.actions _run_actions(actions(), context, report) @@ -343,9 +342,9 @@ def actions() -> Iterator[Union[TestSuiteAction, SkippedActionReport]]: def _run_actions( - actions: Iterator[Union[TestSuiteAction, SkippedActionReport]], + actions: Iterator[TestSuiteAction | SkippedActionReport], context: ExecutionContext, - report: Union[TestSuiteReport, ActionGeneratorReport], + report: TestSuiteReport | ActionGeneratorReport, ) -> None: success = True for a, action in enumerate(actions): @@ -372,11 +371,11 @@ def _run_actions( @dataclass -class ActionStackFrame(object): +class ActionStackFrame: action: TestSuiteAction - parent: Optional[ActionStackFrame] - children: List[ActionStackFrame] - report: Optional[TestSuiteActionReport] = None + parent: ActionStackFrame | None + children: list[ActionStackFrame] + report: TestSuiteActionReport | None = None def address(self) -> JSONAddress: if self.action.test_scenario is not None: @@ -405,13 +404,13 @@ def address(self) -> JSONAddress: return f"{self.parent.address()}.actions[{index}].{addr}" -class ExecutionContext(object): +class ExecutionContext: start_time: datetime - config: Optional[ExecutionConfiguration] - top_frame: Optional[ActionStackFrame] - current_frame: Optional[ActionStackFrame] + config: ExecutionConfiguration | None + top_frame: ActionStackFrame | None + current_frame: ActionStackFrame | None - def __init__(self, config: Optional[ExecutionConfiguration]): + def __init__(self, config: ExecutionConfiguration | None): self.config = config self.top_frame = None self.current_frame = None @@ -422,18 +421,17 @@ def sibling_queries(self) -> Iterator[Query]: return for child in self.current_frame.parent.children: if child.report is not None: - for q in child.report.queries(): - yield q + yield from child.report.queries() def find_test_scenario_reports( - self, scenario_type: Type[TestScenario] - ) -> List[TestScenarioReport]: + self, scenario_type: type[TestScenario] + ) -> list[TestScenarioReport]: """Find reports for all currently-completed instances of the specified test scenario type.""" return self._find_test_scenario_reports(scenario_type, self.top_frame) def _find_test_scenario_reports( - self, scenario_type: Type[TestScenario], frame: ActionStackFrame - ) -> List[TestScenarioReport]: + self, scenario_type: type[TestScenario], frame: ActionStackFrame + ) -> list[TestScenarioReport]: results = [] if ( frame.report is not None @@ -479,9 +477,9 @@ def _compute_n_of( def _ancestor_selected_by( self, - frame: Optional[ActionStackFrame], - of_generation: Optional[int], - which: List[TestSuiteActionSelectionCondition], + frame: ActionStackFrame | None, + of_generation: int | None, + which: list[TestSuiteActionSelectionCondition], ) -> bool: if frame is None: return False @@ -586,7 +584,7 @@ def _is_selected_by( return result - def evaluate_skip(self) -> Optional[SkippedActionReport]: + def evaluate_skip(self) -> SkippedActionReport | None: """Decide whether to skip the action in the current_frame or not. Should be called in between self.begin_action and self.end_action, and before executing the action. diff --git a/monitoring/uss_qualifier/validation.py b/monitoring/uss_qualifier/validation.py index f39e8c03e0..37fb2a3a86 100644 --- a/monitoring/uss_qualifier/validation.py +++ b/monitoring/uss_qualifier/validation.py @@ -1,5 +1,3 @@ -from typing import List - from implicitdict import ImplicitDict from monitoring.monitorlib.schema_validation import ( @@ -13,7 +11,7 @@ from monitoring.uss_qualifier.resources.resource import get_resource_types -def validate_config(config: dict) -> List[ValidationError]: +def validate_config(config: dict) -> list[ValidationError]: """Validate raw data intended to be used to create a USSQualifierConfiguration. Args: diff --git a/monitoring/validate_access_token.py b/monitoring/validate_access_token.py index 1d858c2eeb..326f9bf73c 100644 --- a/monitoring/validate_access_token.py +++ b/monitoring/validate_access_token.py @@ -1,13 +1,12 @@ import argparse import sys -from typing import List import jwt from monitoring.monitorlib import auth_validation -def parse_args(argv: List[str]): +def parse_args(argv: list[str]): parser = argparse.ArgumentParser(description="Validate an access token") parser.add_argument( "--token", diff --git a/schemas/manage_type_schemas.py b/schemas/manage_type_schemas.py index 77eab8c08d..29e531c15b 100644 --- a/schemas/manage_type_schemas.py +++ b/schemas/manage_type_schemas.py @@ -4,7 +4,7 @@ import json import os import sys -from typing import Dict, Optional, Set, Type, get_args, get_origin, get_type_hints +from typing import get_args, get_origin, get_type_hints import implicitdict import implicitdict.jsonschema @@ -52,10 +52,10 @@ def parse_args() -> argparse.Namespace: def _make_type_schemas( - parent: Type[ImplicitDict], + parent: type[ImplicitDict], reference_resolver: SchemaVarsResolver, - repo: Dict[str, dict], - already_checked: Optional[Set[str]] = None, + repo: dict[str, dict], + already_checked: set[str] | None = None, ) -> None: implicitdict.jsonschema.make_json_schema(parent, reference_resolver, repo) if already_checked is None: @@ -88,8 +88,8 @@ def _make_type_schemas( def _find_specifications( module, - repo: Dict[str, Type[ImplicitDict]], - already_checked: Optional[Set[str]] = None, + repo: dict[str, type[ImplicitDict]], + already_checked: set[str] | None = None, ) -> None: if already_checked is None: already_checked = set() @@ -119,11 +119,11 @@ def main() -> int: "Invalid usage; action must be specified with --check or --generate flags" ) - def schema_vars_resolver(schema_type: Type) -> SchemaVars: + def schema_vars_resolver(schema_type: type) -> SchemaVars: if schema_type.__module__ in {"builtins", "typing"}: return SchemaVars(name=schema_type.__name__) - def path_of_py_file(t: Type) -> str: + def path_of_py_file(t: type) -> str: top_module = t.__module__.split(".")[0] module_path = os.path.dirname(sys.modules[top_module].__file__) py_file_path = inspect.getfile(t) @@ -131,13 +131,13 @@ def path_of_py_file(t: Type) -> str: top_module, os.path.relpath(py_file_path, start=module_path) ) - def full_name(t: Type) -> str: + def full_name(t: type) -> str: return t.__module__ + "." + t.__qualname__ - def path_of_schema_file(t: Type) -> str: + def path_of_schema_file(t: type) -> str: return "schemas/" + "/".join(full_name(t).split(".")) + ".json" - def path_to(t_dest: Type, t_src: Type) -> str: + def path_to(t_dest: type, t_src: type) -> str: path_to_dest = path_of_schema_file(t_dest) path_to_src = os.path.dirname(path_of_schema_file(t_src)) rel_path = os.path.relpath(path_to_dest, start=path_to_src) @@ -203,7 +203,7 @@ def path_to(t_dest: Type, t_src: Type) -> str: for rel_filename, schema in schemas.items(): filename = os.path.abspath(os.path.join(repo_root, rel_filename)) if os.path.exists(filename): - with open(filename, "r") as f: + with open(filename) as f: old_value = json.load(f) if schema == old_value: continue diff --git a/test/repo_hygiene/md_files/md_files.py b/test/repo_hygiene/md_files/md_files.py index f09945c9d0..512e6629b7 100644 --- a/test/repo_hygiene/md_files/md_files.py +++ b/test/repo_hygiene/md_files/md_files.py @@ -7,7 +7,7 @@ def check_md_file(md_file_path: str, repo_root: str) -> None: - with open(md_file_path, "r") as f: + with open(md_file_path) as f: doc = marko.parse(f.read()) check_local_links(doc, md_file_path, repo_root)