From 371f4d620d4a158fe7aa4031703365a29f855a1b Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 8 Oct 2026 10:13:57 +0200 Subject: [PATCH] refactor: Rename `build_tag` to `build` in `validate_input()` --- src/apify_client/_resource_clients/actor.py | 39 ++++++++-- tests/unit/test_actor_validate_input.py | 81 +++++++++++++++++++++ 2 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/unit/test_actor_validate_input.py diff --git a/src/apify_client/_resource_clients/actor.py b/src/apify_client/_resource_clients/actor.py index a4be92f3..fb94eb01 100644 --- a/src/apify_client/_resource_clients/actor.py +++ b/src/apify_client/_resource_clients/actor.py @@ -1,5 +1,6 @@ from __future__ import annotations +import warnings from typing import TYPE_CHECKING, Any, Literal from pydantic import TypeAdapter @@ -567,9 +568,10 @@ def validate_input( self, run_input: Any = None, *, - build_tag: str | None = None, + build: str | None = None, content_type: str | None = None, timeout: Timeout = 'short', + build_tag: str | None = None, ) -> bool: """Validate an input for the Actor that defines an input schema. @@ -578,13 +580,25 @@ def validate_input( including an `io.IOBase` stream such as an open file, an iterable of byte chunks, or a streamed `HttpResponse`, which are uploaded in chunks without being held in memory. Streaming is experimental, and its behavior may change in future versions. - build_tag: The Actor's build tag. + build: The Actor build to validate the input against. It can be either a build tag or build number. By + default, the build specified in the default run configuration for the Actor (typically latest) is used. content_type: The content type of the input. timeout: Timeout for the API HTTP request. + build_tag: Deprecated alias of `build`. Will be removed in v4. Returns: True if the input is valid, else raise an exception with validation error details. """ + if build_tag is not None: + warnings.warn( + 'The `build_tag` argument is deprecated and will be removed in v4. Use `build` instead.', + DeprecationWarning, + stacklevel=3, + ) + if build is not None: + raise ValueError('Pass only one of `build` and `build_tag`.') + build = build_tag + run_input, content_type = encode_key_value_store_record_value(run_input, content_type=content_type) self._http_client.call( @@ -592,7 +606,7 @@ def validate_input( method='POST', headers={'content-type': content_type}, data=run_input, - params=self._build_params(build=build_tag), + params=self._build_params(build=build), timeout=timeout, ) @@ -1102,9 +1116,10 @@ async def validate_input( self, run_input: Any = None, *, - build_tag: str | None = None, + build: str | None = None, content_type: str | None = None, timeout: Timeout = 'short', + build_tag: str | None = None, ) -> bool: """Validate an input for the Actor that defines an input schema. @@ -1113,13 +1128,25 @@ async def validate_input( including an `io.IOBase` stream such as an open file, an iterable of byte chunks, or a streamed `HttpResponse`, which are uploaded in chunks without being held in memory. Streaming is experimental, and its behavior may change in future versions. - build_tag: The Actor's build tag. + build: The Actor build to validate the input against. It can be either a build tag or build number. By + default, the build specified in the default run configuration for the Actor (typically latest) is used. content_type: The content type of the input. timeout: Timeout for the API HTTP request. + build_tag: Deprecated alias of `build`. Will be removed in v4. Returns: True if the input is valid, else raise an exception with validation error details. """ + if build_tag is not None: + warnings.warn( + 'The `build_tag` argument is deprecated and will be removed in v4. Use `build` instead.', + DeprecationWarning, + stacklevel=3, + ) + if build is not None: + raise ValueError('Pass only one of `build` and `build_tag`.') + build = build_tag + run_input, content_type = encode_key_value_store_record_value(run_input, content_type=content_type) await self._http_client.call( @@ -1127,7 +1154,7 @@ async def validate_input( method='POST', headers={'content-type': content_type}, data=run_input, - params=self._build_params(build=build_tag), + params=self._build_params(build=build), timeout=timeout, ) diff --git a/tests/unit/test_actor_validate_input.py b/tests/unit/test_actor_validate_input.py new file mode 100644 index 00000000..f842005b --- /dev/null +++ b/tests/unit/test_actor_validate_input.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from apify_client import ApifyClient, ApifyClientAsync + +if TYPE_CHECKING: + from pytest_httpserver import HTTPServer + +pytestmark = pytest.mark.usefixtures('http_client_classes') + +ACTOR_ID = 'test_actor_id' + + +def expect_validate_input(httpserver: HTTPServer, build: str) -> str: + httpserver.expect_request( + f'/v2/actors/{ACTOR_ID}/validate-input', + method='POST', + query_string={'build': build}, + ).respond_with_json({'valid': True}) + return httpserver.url_for('/').removesuffix('/') + + +def test_validate_input_passes_build_sync(httpserver: HTTPServer) -> None: + """`build` is sent as the `build` query parameter.""" + client = ApifyClient(token='test_token', api_url=expect_validate_input(httpserver, '1.2.3')) + + assert client.actor(ACTOR_ID).validate_input({}, build='1.2.3') is True + httpserver.check_assertions() + + +async def test_validate_input_passes_build_async(httpserver: HTTPServer) -> None: + """`build` is sent as the `build` query parameter.""" + client = ApifyClientAsync(token='test_token', api_url=expect_validate_input(httpserver, '1.2.3')) + + assert await client.actor(ACTOR_ID).validate_input({}, build='1.2.3') is True + httpserver.check_assertions() + + +def test_validate_input_build_tag_is_deprecated_alias_sync(httpserver: HTTPServer) -> None: + """The deprecated `build_tag` emits a `DeprecationWarning` pointing at the caller and is sent as `build`.""" + client = ApifyClient(token='test_token', api_url=expect_validate_input(httpserver, 'beta')) + + with pytest.warns(DeprecationWarning, match='`build_tag` argument is deprecated') as record: + assert client.actor(ACTOR_ID).validate_input({}, build_tag='beta') is True + assert record[0].filename == __file__ + httpserver.check_assertions() + + +async def test_validate_input_build_tag_is_deprecated_alias_async(httpserver: HTTPServer) -> None: + """The deprecated `build_tag` emits a `DeprecationWarning` pointing at the caller and is sent as `build`.""" + client = ApifyClientAsync(token='test_token', api_url=expect_validate_input(httpserver, 'beta')) + + with pytest.warns(DeprecationWarning, match='`build_tag` argument is deprecated') as record: + assert await client.actor(ACTOR_ID).validate_input({}, build_tag='beta') is True + assert record[0].filename == __file__ + httpserver.check_assertions() + + +def test_validate_input_rejects_build_and_build_tag_sync() -> None: + """Passing both `build` and `build_tag` raises a `ValueError`.""" + client = ApifyClient(token='test_token') + + with ( + pytest.warns(DeprecationWarning, match='`build_tag` argument is deprecated'), + pytest.raises(ValueError, match='only one of'), + ): + client.actor(ACTOR_ID).validate_input({}, build='latest', build_tag='beta') + + +async def test_validate_input_rejects_build_and_build_tag_async() -> None: + """Passing both `build` and `build_tag` raises a `ValueError`.""" + client = ApifyClientAsync(token='test_token') + + with ( + pytest.warns(DeprecationWarning, match='`build_tag` argument is deprecated'), + pytest.raises(ValueError, match='only one of'), + ): + await client.actor(ACTOR_ID).validate_input({}, build='latest', build_tag='beta')