diff --git a/pyproject.toml b/pyproject.toml index 894c5d2e6..583e2f3ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,6 +158,7 @@ line-length = 79 lint.select = [ "ALL", ] +lint.extend-select = [ "ANN", "PGH003", "PYI" ] lint.ignore = [ # Ruff warns that this conflicts with the formatter. "COM812", @@ -180,6 +181,7 @@ lint.ignore = [ "TC002", "TC003", ] +lint.explicit-preview-rules = true lint.per-file-ignores."doccmd_*.py" = [ # Allow our chosen docstring line-style - pydocstringformatter handles # formatting but docstrings in docs may not match this style. @@ -205,6 +207,7 @@ lint.flake8-tidy-imports.banned-api."typing.cast".msg = """\ typing.cast is banned: use explicit type narrowing or a typed variable instead.\ """ lint.pydocstyle.convention = "google" +lint.preview = true [tool.pylint] # Disable the message, report, category or checker with the given id(s). You @@ -456,6 +459,23 @@ typeCheckingMode = "strict" enableTypeIgnoreComments = false reportUnnecessaryTypeIgnoreComment = true +[tool.ty] +rules.blanket-ignore-comment = "error" +rules.division-by-zero = "warn" +rules.dynamic-function-decorator-return = "error" +rules.missing-type-argument = "error" +rules.possibly-missing-attribute = "warn" +rules.possibly-missing-import = "warn" +rules.possibly-unresolved-reference = "warn" +rules.unsound-assignment = "error" +rules.unsound-return-statement = "error" +rules.unsound-yield = "error" +rules.unsupported-dynamic-base = "warn" +terminal.error-on-warning = true +analysis.respect-type-ignore-comments = false +analysis.strict-equality-semantics = true +analysis.strict-generic-narrowing = true + [tool.pytest] addopts = [ "--strict-markers", diff --git a/spelling_private_dict.txt b/spelling_private_dict.txt index c80752bf8..b50dc893c 100644 --- a/spelling_private_dict.txt +++ b/spelling_private_dict.txt @@ -129,6 +129,7 @@ timestamp todo travis txt +ty unlinks unmocked unrouted diff --git a/src/mock_vws/_httpx2_mock_server/decorators.py b/src/mock_vws/_httpx2_mock_server/decorators.py index c873f7558..b9444bce6 100644 --- a/src/mock_vws/_httpx2_mock_server/decorators.py +++ b/src/mock_vws/_httpx2_mock_server/decorators.py @@ -288,7 +288,7 @@ def _mock_routes( handler: _Handler = getattr( # pylint: disable=bad-builtin api, route.route_name, - ) + ) # ty: ignore[unsound-assignment] httpx2_handler = delayed_httpx2_handler( handler=_httpx2_handler(handler=handler, base_path=base_path), delay_seconds=response_delay_seconds, diff --git a/src/mock_vws/_model_target_web_api.py b/src/mock_vws/_model_target_web_api.py index 25e39a8bd..cab79217a 100644 --- a/src/mock_vws/_model_target_web_api.py +++ b/src/mock_vws/_model_target_web_api.py @@ -345,8 +345,8 @@ def _jwt_scopes(*, bearer_token: str) -> frozenset[str]: ) scope = payload.get("scope", "") # pyrefly: ignore [unknown-variable-type] if not isinstance(scope, str): - return frozenset() - return frozenset(scope.split()) + return frozenset() # ty: ignore[unsound-return-statement] + return frozenset(scope.split()) # ty: ignore[unsound-return-statement] @beartype diff --git a/src/mock_vws/_services_validators/name_validators.py b/src/mock_vws/_services_validators/name_validators.py index be8ead175..80e029b42 100644 --- a/src/mock_vws/_services_validators/name_validators.py +++ b/src/mock_vws/_services_validators/name_validators.py @@ -33,7 +33,7 @@ def _given_name(*, context: ValidatorContext) -> str | None: :py:func:`validate_name_type`. """ request_json = context.request_json - name: str | None = request_json.get("name") + name: str | None = request_json.get("name") # ty: ignore[unsound-assignment] return name @@ -63,7 +63,7 @@ def _new_target_name(*, context: ValidatorContext) -> str: a request which does not give one, and :py:func:`validate_name_type` has already rejected one which is not a string. """ - name: str = context.request_json["name"] + name: str = context.request_json["name"] # ty: ignore[unsound-assignment] return name diff --git a/tests/conftest.py b/tests/conftest.py index 3256f746d..6b6091516 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -206,7 +206,7 @@ def endpoint(*, request: pytest.FixtureRequest) -> Endpoint: the cross-cutting ``Authorization`` and ``Date`` header concerns do not apply to it. """ - endpoint_fixture: Endpoint = request.getfixturevalue(argname=request.param) + endpoint_fixture: Endpoint = request.getfixturevalue(argname=request.param) # ty: ignore[unsound-assignment] return endpoint_fixture @@ -234,7 +234,7 @@ def model_target_endpoint( """ endpoint_fixture: ModelTargetEndpoint = request.getfixturevalue( argname=request.param, - ) + ) # ty: ignore[unsound-assignment] return endpoint_fixture @@ -265,7 +265,7 @@ def not_base64_encoded_processable(*, request: pytest.FixtureRequest) -> str: ``UNPROCESSABLE_ENTITY`` when this is given. """ - not_base64_encoded_string: str = request.param + not_base64_encoded_string: str = request.param # ty: ignore[unsound-assignment] with pytest.raises(expected_exception=binascii.Error): _ = base64.b64decode(s=not_base64_encoded_string, validate=True) @@ -291,7 +291,7 @@ def not_base64_encoded_not_processable( will return an ``UNPROCESSABLE_ENTITY`` response when this is given. """ - not_base64_encoded_string: str = request.param + not_base64_encoded_string: str = request.param # ty: ignore[unsound-assignment] with pytest.raises(expected_exception=binascii.Error): _ = base64.b64decode(s=not_base64_encoded_string, validate=True) diff --git a/tests/mock_vws/test_docker.py b/tests/mock_vws/test_docker.py index 46609f658..66e544c6f 100644 --- a/tests/mock_vws/test_docker.py +++ b/tests/mock_vws/test_docker.py @@ -252,13 +252,13 @@ def fixture_custom_bridge_network() -> Iterator[Network]: yield network finally: network.reload() - images_to_remove: Iterable[Image] = set() + images_to_remove: Iterable[Image] = set() # ty: ignore[unsound-assignment] for container in network.containers: network.disconnect(container=container) container.stop() container.remove(v=True, force=True) assert container.image is not None - images_to_remove = {*images_to_remove, container.image} + images_to_remove = {*images_to_remove, container.image} # ty: ignore[unsound-assignment] # This does leave behind untagged images. for image in images_to_remove: diff --git a/tests/mock_vws/test_healthcheck.py b/tests/mock_vws/test_healthcheck.py index 8cbd5cd91..71fbe7c0b 100644 --- a/tests/mock_vws/test_healthcheck.py +++ b/tests/mock_vws/test_healthcheck.py @@ -42,7 +42,7 @@ def _unused_port() -> int: """Return a port with nothing listening on it.""" with socket.socket() as sock: sock.bind(("localhost", 0)) - port: int = sock.getsockname()[1] + port: int = sock.getsockname()[1] # ty: ignore[unsound-assignment] return port diff --git a/tests/mock_vws/test_model_target_web_api.py b/tests/mock_vws/test_model_target_web_api.py index af6cc5d3b..206f72152 100644 --- a/tests/mock_vws/test_model_target_web_api.py +++ b/tests/mock_vws/test_model_target_web_api.py @@ -469,7 +469,7 @@ def test_client_credentials_management( response=create_response, status_codes=HTTPStatus.CREATED, ) - client_id = create_response.json()["client_id"] + client_id = create_response.json()["client_id"] # ty: ignore[unsound-assignment] client_secret = create_response.json()["client_secret"] # pyrefly: ignore [unknown-variable-type] list_response = model_target_get( @@ -2062,7 +2062,7 @@ def test_dataset_is_visible_to_the_other_dataset_type( response=create_response, status_codes=HTTPStatus.CREATED, ) - dataset_uuid = create_response.json()["uuid"] + dataset_uuid = create_response.json()["uuid"] # ty: ignore[unsound-assignment] other_status_response = model_target_get( url=f"{_VWS_HOST}{other_path}/{dataset_uuid}/status", diff --git a/tests/mock_vws/test_query.py b/tests/mock_vws/test_query.py index 7972ebf8b..4427295aa 100644 --- a/tests/mock_vws/test_query.py +++ b/tests/mock_vws/test_query.py @@ -1025,7 +1025,7 @@ def _add_and_wait_for_targets( num_targets: int, ) -> None: """Add targets with the given image.""" - target_ids: Iterable[str] = set() + target_ids: Iterable[str] = set() # ty: ignore[unsound-assignment] for _ in range(num_targets): target_id = vws_client.add_target( name=uuid.uuid4().hex, @@ -1034,7 +1034,7 @@ def _add_and_wait_for_targets( active_flag=True, application_metadata=None, ) - target_ids = {*target_ids, target_id} + target_ids = {*target_ids, target_id} # ty: ignore[unsound-assignment] for created_target_id in target_ids: vws_client.wait_for_target_processed(target_id=created_target_id) diff --git a/tests/mock_vws/utils/model_target_retries.py b/tests/mock_vws/utils/model_target_retries.py index dbe6171c8..cdb12bff3 100644 --- a/tests/mock_vws/utils/model_target_retries.py +++ b/tests/mock_vws/utils/model_target_retries.py @@ -91,7 +91,7 @@ def _last_outcome(retry_state: RetryCallState) -> Response | requests.Response: status and body, and a transport failure propagates. """ assert retry_state.outcome is not None - outcome: Response | requests.Response = retry_state.outcome.result() + outcome: Response | requests.Response = retry_state.outcome.result() # ty: ignore[unsound-assignment] return outcome