From 3db1aa0ba6bf6b3f2029ce294cb02e76c2cb83cf Mon Sep 17 00:00:00 2001 From: ccurme Date: Sat, 31 May 2025 15:21:15 -0400 Subject: [PATCH] standard-tests: migrate to pytest-recording (#31425) Co-authored-by: Eugene Yurtsev --- ...AnthropicStandard.test_stream_time.yaml.gz | Bin 0 -> 3355 bytes ...AnthropicStandard_test_stream_time.yaml.gz | 1 - libs/partners/anthropic/tests/conftest.py | 11 +-- libs/partners/anthropic/uv.lock | 32 +++--- ...stOpenAIResponses.test_stream_time.yaml.gz | Bin 0 -> 22145 bytes ...stOpenAIResponses_test_stream_time.yaml.gz | 1 - ...estOpenAIStandard.test_stream_time.yaml.gz | Bin 0 -> 18423 bytes ...estOpenAIStandard_test_stream_time.yaml.gz | 1 - libs/partners/openai/tests/conftest.py | 18 ++-- libs/partners/openai/uv.lock | 32 +++--- .../langchain_tests/conftest.py | 93 +++++++++++++++--- .../integration_tests/chat_models.py | 44 +++++---- .../langchain_tests/unit_tests/chat_models.py | 28 +++--- libs/standard-tests/pyproject.toml | 11 ++- libs/standard-tests/uv.lock | 32 +++--- 15 files changed, 185 insertions(+), 119 deletions(-) create mode 100644 libs/partners/anthropic/tests/cassettes/TestAnthropicStandard.test_stream_time.yaml.gz delete mode 100644 libs/partners/anthropic/tests/cassettes/TestAnthropicStandard_test_stream_time.yaml.gz create mode 100644 libs/partners/openai/tests/cassettes/TestOpenAIResponses.test_stream_time.yaml.gz delete mode 100644 libs/partners/openai/tests/cassettes/TestOpenAIResponses_test_stream_time.yaml.gz create mode 100644 libs/partners/openai/tests/cassettes/TestOpenAIStandard.test_stream_time.yaml.gz delete mode 100644 libs/partners/openai/tests/cassettes/TestOpenAIStandard_test_stream_time.yaml.gz diff --git a/libs/partners/anthropic/tests/cassettes/TestAnthropicStandard.test_stream_time.yaml.gz b/libs/partners/anthropic/tests/cassettes/TestAnthropicStandard.test_stream_time.yaml.gz new file mode 100644 index 0000000000000000000000000000000000000000..10a3e4007191e4e50aca514fe50afa09e25ea236 GIT binary patch literal 3355 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 literal 0 HcmV?d00001 diff --git a/libs/partners/anthropic/tests/cassettes/TestAnthropicStandard_test_stream_time.yaml.gz b/libs/partners/anthropic/tests/cassettes/TestAnthropicStandard_test_stream_time.yaml.gz deleted file mode 100644 index 036536b2fc6..00000000000 --- a/libs/partners/anthropic/tests/cassettes/TestAnthropicStandard_test_stream_time.yaml.gz +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/libs/partners/anthropic/tests/conftest.py b/libs/partners/anthropic/tests/conftest.py index c4da8f2e905..8a72f4ba99b 100644 --- a/libs/partners/anthropic/tests/conftest.py +++ b/libs/partners/anthropic/tests/conftest.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from langchain_tests.conftest import YamlGzipSerializer +from langchain_tests.conftest import CustomPersister, CustomSerializer from langchain_tests.conftest import _base_vcr_config as _base_vcr_config from vcr import VCR # type: ignore[import-untyped] @@ -32,9 +32,6 @@ def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 return config -@pytest.fixture -def vcr(vcr_config: dict) -> VCR: - """Override the default vcr fixture to include custom serializers""" - my_vcr = VCR(**vcr_config) - my_vcr.register_serializer("yaml.gz", YamlGzipSerializer) - return my_vcr +def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) diff --git a/libs/partners/anthropic/uv.lock b/libs/partners/anthropic/uv.lock index 0dc81d27149..60d0f20fd7a 100644 --- a/libs/partners/anthropic/uv.lock +++ b/libs/partners/anthropic/uv.lock @@ -502,7 +502,7 @@ typing = [ [[package]] name = "langchain-core" -version = "0.3.62" +version = "0.3.63" source = { editable = "../../core" } dependencies = [ { name = "jsonpatch" }, @@ -571,8 +571,8 @@ dependencies = [ { name = "pytest-asyncio" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket" }, - { name = "pytest-vcr" }, { name = "syrupy" }, { name = "vcrpy" }, ] @@ -587,8 +587,8 @@ requires-dist = [ { name = "pytest-asyncio", specifier = ">=0.20,<1" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket", specifier = ">=0.6.0,<1" }, - { name = "pytest-vcr" }, { name = "syrupy", specifier = ">=4,<5" }, { name = "vcrpy", specifier = ">=7.0" }, ] @@ -1339,6 +1339,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, ] +[[package]] +name = "pytest-recording" +version = "0.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "vcrpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 }, +] + [[package]] name = "pytest-retry" version = "1.7.0" @@ -1375,19 +1388,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148 }, ] -[[package]] -name = "pytest-vcr" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "vcrpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 }, -] - [[package]] name = "pytest-watcher" version = "0.4.3" diff --git a/libs/partners/openai/tests/cassettes/TestOpenAIResponses.test_stream_time.yaml.gz b/libs/partners/openai/tests/cassettes/TestOpenAIResponses.test_stream_time.yaml.gz new file mode 100644 index 0000000000000000000000000000000000000000..b954ebfd34fbfe2b1eacb0d070edcce3c9e6794d GIT binary patch literal 22145 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 literal 0 HcmV?d00001 diff --git a/libs/partners/openai/tests/cassettes/TestOpenAIStandard_test_stream_time.yaml.gz b/libs/partners/openai/tests/cassettes/TestOpenAIStandard_test_stream_time.yaml.gz deleted file mode 100644 index a014705acd7..00000000000 --- a/libs/partners/openai/tests/cassettes/TestOpenAIStandard_test_stream_time.yaml.gz +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/libs/partners/openai/tests/conftest.py b/libs/partners/openai/tests/conftest.py index 563761518c7..064431d3b69 100644 --- a/libs/partners/openai/tests/conftest.py +++ b/libs/partners/openai/tests/conftest.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from langchain_tests.conftest import YamlGzipSerializer +from langchain_tests.conftest import CustomPersister, CustomSerializer from langchain_tests.conftest import _base_vcr_config as _base_vcr_config from vcr import VCR # type: ignore[import-untyped] @@ -13,6 +13,7 @@ _EXTRA_HEADERS = [ def remove_request_headers(request: Any) -> Any: + """Remove sensitive headers from the request.""" for k in request.headers: request.headers[k] = "**REDACTED**" request.uri = "**REDACTED**" @@ -20,6 +21,7 @@ def remove_request_headers(request: Any) -> Any: def remove_response_headers(response: dict) -> dict: + """Remove sensitive headers from the response.""" for k in response["headers"]: response["headers"][k] = "**REDACTED**" return response @@ -27,22 +29,16 @@ def remove_response_headers(response: dict) -> dict: @pytest.fixture(scope="session") def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 - """ - Extend the default configuration coming from langchain_tests. - """ + """Extend the default configuration coming from langchain_tests.""" config = _base_vcr_config.copy() config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) config["before_record_request"] = remove_request_headers config["before_record_response"] = remove_response_headers config["serializer"] = "yaml.gz" config["path_transformer"] = VCR.ensure_suffix(".yaml.gz") - return config -@pytest.fixture -def vcr(vcr_config: dict) -> VCR: - """Override the default vcr fixture to include custom serializers""" - my_vcr = VCR(**vcr_config) - my_vcr.register_serializer("yaml.gz", YamlGzipSerializer) - return my_vcr +def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) diff --git a/libs/partners/openai/uv.lock b/libs/partners/openai/uv.lock index 41cc63a0588..4426df89bdc 100644 --- a/libs/partners/openai/uv.lock +++ b/libs/partners/openai/uv.lock @@ -479,7 +479,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.62" +version = "0.3.63" source = { editable = "../../core" } dependencies = [ { name = "jsonpatch" }, @@ -637,8 +637,8 @@ dependencies = [ { name = "pytest-asyncio" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket" }, - { name = "pytest-vcr" }, { name = "syrupy" }, { name = "vcrpy" }, ] @@ -653,8 +653,8 @@ requires-dist = [ { name = "pytest-asyncio", specifier = ">=0.20,<1" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket", specifier = ">=0.6.0,<1" }, - { name = "pytest-vcr" }, { name = "syrupy", specifier = ">=4,<5" }, { name = "vcrpy", specifier = ">=7.0" }, ] @@ -1514,6 +1514,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, ] +[[package]] +name = "pytest-recording" +version = "0.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "vcrpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 }, +] + [[package]] name = "pytest-retry" version = "1.7.0" @@ -1538,19 +1551,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/58/5d14cb5cb59409e491ebe816c47bf81423cd03098ea92281336320ae5681/pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45", size = 6754 }, ] -[[package]] -name = "pytest-vcr" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "vcrpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 }, -] - [[package]] name = "pytest-watcher" version = "0.4.3" diff --git a/libs/standard-tests/langchain_tests/conftest.py b/libs/standard-tests/langchain_tests/conftest.py index 16ff1cf97bd..9df423c5c2a 100644 --- a/libs/standard-tests/langchain_tests/conftest.py +++ b/libs/standard-tests/langchain_tests/conftest.py @@ -1,25 +1,85 @@ -import base64 import gzip +from os import PathLike +from pathlib import Path +from typing import Union import pytest -from vcr import VCR # type: ignore[import-untyped] -from vcr.serializers import yamlserializer # type: ignore[import-untyped] +import yaml +from vcr import VCR +from vcr.persisters.filesystem import CassetteNotFoundError +from vcr.request import Request -class YamlGzipSerializer: - @staticmethod - def serialize(cassette_dict: dict) -> str: - raw = yamlserializer.serialize(cassette_dict).encode("utf-8") - compressed = gzip.compress(raw) - return base64.b64encode(compressed).decode("ascii") +class CustomSerializer: + """Custom serializer for VCR cassettes using YAML and gzip. + + We're using a custom serializer to avoid the default yaml serializer + used by VCR, which is not designed to be safe for untrusted input. + + This step is an extra precaution necessary because the cassette files + are in compressed YAML format, which makes it more difficult to inspect + their contents during development or debugging. + """ @staticmethod - def deserialize(data: str) -> dict: - compressed = base64.b64decode(data.encode("ascii")) - text = gzip.decompress(compressed).decode("utf-8") - return yamlserializer.deserialize(text) + def serialize(cassette_dict: dict) -> bytes: + """Convert cassette to YAML and compress it.""" + cassette_dict["requests"] = [ + request._to_dict() for request in cassette_dict["requests"] + ] + yml = yaml.safe_dump(cassette_dict) + return gzip.compress(yml.encode("utf-8")) + + @staticmethod + def deserialize(data: bytes) -> dict: + """Decompress data and convert it from YAML.""" + text = gzip.decompress(data).decode("utf-8") + cassette = yaml.safe_load(text) + cassette["requests"] = [ + Request._from_dict(request) for request in cassette["requests"] + ] + return cassette +class CustomPersister: + """A custom persister for VCR that uses the CustomSerializer.""" + + @classmethod + def load_cassette( + cls, cassette_path: Union[str, PathLike[str]], serializer: CustomSerializer + ) -> tuple[dict, dict]: + """Load a cassette from a file.""" + # If cassette path is already Path this is a no-op + cassette_path = Path(cassette_path) + if not cassette_path.is_file(): + raise CassetteNotFoundError( + f"Cassette file {cassette_path} does not exist." + ) + with cassette_path.open(mode="rb") as f: + data = f.read() + deser = serializer.deserialize(data) + return deser["requests"], deser["responses"] + + @staticmethod + def save_cassette( + cassette_path: Union[str, PathLike[str]], + cassette_dict: dict, + serializer: CustomSerializer, + ) -> None: + """Save a cassette to a file.""" + data = serializer.serialize(cassette_dict) + # if cassette path is already Path this is no operation + cassette_path = Path(cassette_path) + cassette_folder = cassette_path.parent + if not cassette_folder.exists(): + cassette_folder.mkdir(parents=True) + with cassette_path.open("wb") as f: + f.write(data) + + +# A list of headers that should be filtered out of the cassettes. +# These are typically associated with sensitive information and should +# not be stored in cassettes. _BASE_FILTER_HEADERS = [ ("authorization", "PLACEHOLDER"), ("x-api-key", "PLACEHOLDER"), @@ -29,14 +89,15 @@ _BASE_FILTER_HEADERS = [ @pytest.fixture(scope="session") def _base_vcr_config() -> dict: - """ - Configuration that every cassette will receive. + """Configuration that every cassette will receive. + (Anything permitted by vcr.VCR(**kwargs) can be put here.) """ return { "record_mode": "once", "filter_headers": _BASE_FILTER_HEADERS.copy(), - "match_on": ["method", "scheme", "host", "port", "path", "query"], + "match_on": ["method", "uri", "body"], + "allow_playback_repeats": True, "decode_compressed_response": True, "cassette_library_dir": "tests/cassettes", "path_transformer": VCR.ensure_suffix(".yaml"), diff --git a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py index 69ca50bc6d2..218e48ca3c8 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py @@ -6,7 +6,6 @@ from unittest.mock import MagicMock import httpx import pytest -import vcr # type: ignore[import-untyped] from langchain_core._api import warn_deprecated from langchain_core.callbacks import BaseCallbackHandler from langchain_core.language_models import BaseChatModel, GenericFakeChatModel @@ -31,6 +30,7 @@ from pydantic.v1 import BaseModel as BaseModelV1 from pydantic.v1 import Field as FieldV1 from pytest_benchmark.fixture import BenchmarkFixture # type: ignore[import-untyped] from typing_extensions import Annotated, TypedDict +from vcr.cassette import Cassette from langchain_tests.unit_tests.chat_models import ( ChatModelTests, @@ -592,7 +592,7 @@ class ChatModelIntegrationTests(ChatModelTests): :caption: tests/conftest.py import pytest - from langchain_tests.conftest import YamlGzipSerializer + from langchain_tests.conftest import CustomPersister, CustomSerializer from langchain_tests.conftest import _base_vcr_config as _base_vcr_config from vcr import VCR @@ -621,24 +621,26 @@ class ChatModelIntegrationTests(ChatModelTests): return config - @pytest.fixture - def vcr(vcr_config: dict) -> VCR: - \"\"\"Override the default vcr fixture to include custom serializers\"\"\" - my_vcr = VCR(**vcr_config) - my_vcr.register_serializer("yaml.gz", YamlGzipSerializer) - return my_vcr + def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) + You can inspect the contents of the compressed cassettes (e.g., to - ensure no sensitive information is recorded) using the serializer: + ensure no sensitive information is recorded) using + + .. code-block:: bash + + gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + + or by using the serializer: .. code-block:: python - from langchain_tests.conftest import YamlGzipSerializer + from langchain_tests.conftest import CustomPersister, CustomSerializer - with open("/path/to/tests/cassettes/TestClass_test.yaml.gz", "r") as f: - data = f.read() - - YamlGzipSerializer.deserialize(data) + cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" + requests, responses = CustomPersister().load_cassette(path, CustomSerializer()) 3. Run tests to generate VCR cassettes. @@ -2826,8 +2828,9 @@ class ChatModelIntegrationTests(ChatModelTests): assert isinstance(response, AIMessage) @pytest.mark.benchmark + @pytest.mark.vcr def test_stream_time( - self, model: BaseChatModel, benchmark: BenchmarkFixture, vcr: vcr.VCR + self, model: BaseChatModel, benchmark: BenchmarkFixture, vcr: Cassette ) -> None: """Test that streaming does not introduce undue overhead. @@ -2857,12 +2860,13 @@ class ChatModelIntegrationTests(ChatModelTests): pytest.skip("VCR not set up.") def _run() -> None: - cassette_name = f"{self.__class__.__name__}_test_stream_time" - with vcr.use_cassette(cassette_name, record_mode="once"): - for _ in model.stream("Write a story about a cat."): - pass + for _ in model.stream("Write a story about a cat."): + pass - benchmark(_run) + if not vcr.responses: + _run() + else: + benchmark(_run) def invoke_with_audio_input(self, *, stream: bool = False) -> AIMessage: """:private:""" diff --git a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py index 2b7ad0faf51..5a5ff1c0790 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py @@ -693,7 +693,7 @@ class ChatModelUnitTests(ChatModelTests): :caption: tests/conftest.py import pytest - from langchain_tests.conftest import YamlGzipSerializer + from langchain_tests.conftest import CustomPersister, CustomSerializer from langchain_tests.conftest import _base_vcr_config as _base_vcr_config from vcr import VCR @@ -722,24 +722,26 @@ class ChatModelUnitTests(ChatModelTests): return config - @pytest.fixture - def vcr(vcr_config: dict) -> VCR: - \"\"\"Override the default vcr fixture to include custom serializers\"\"\" - my_vcr = VCR(**vcr_config) - my_vcr.register_serializer("yaml.gz", YamlGzipSerializer) - return my_vcr + def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) + You can inspect the contents of the compressed cassettes (e.g., to - ensure no sensitive information is recorded) using the serializer: + ensure no sensitive information is recorded) using + + .. code-block:: bash + + gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + + or by using the serializer: .. code-block:: python - from langchain_tests.conftest import YamlGzipSerializer + from langchain_tests.conftest import CustomPersister, CustomSerializer - with open("/path/to/tests/cassettes/TestClass_test.yaml.gz", "r") as f: - data = f.read() - - YamlGzipSerializer.deserialize(data) + cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" + requests, responses = CustomPersister().load_cassette(path, CustomSerializer()) 3. Run tests to generate VCR cassettes. diff --git a/libs/standard-tests/pyproject.toml b/libs/standard-tests/pyproject.toml index 8c6ff010b1c..e8dce7b9180 100644 --- a/libs/standard-tests/pyproject.toml +++ b/libs/standard-tests/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "pytest-socket<1,>=0.6.0", "pytest-benchmark", "pytest-codspeed", - "pytest-vcr", + "pytest-recording", "vcrpy>=7.0", "numpy>=1.26.2; python_version<'3.13'", "numpy>=2.1.0; python_version>='3.13'", @@ -42,6 +42,15 @@ langchain-core = { path = "../core", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[[tool.mypy.overrides]] +module = "yaml" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "vcr.*" +ignore_missing_imports = true + + [tool.ruff] target-version = "py39" diff --git a/libs/standard-tests/uv.lock b/libs/standard-tests/uv.lock index bd6d9115cc0..b753767a931 100644 --- a/libs/standard-tests/uv.lock +++ b/libs/standard-tests/uv.lock @@ -304,7 +304,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "0.3.60" +version = "0.3.63" source = { editable = "../core" } dependencies = [ { name = "jsonpatch" }, @@ -373,8 +373,8 @@ dependencies = [ { name = "pytest-asyncio" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket" }, - { name = "pytest-vcr" }, { name = "syrupy" }, { name = "vcrpy" }, ] @@ -404,8 +404,8 @@ requires-dist = [ { name = "pytest-asyncio", specifier = ">=0.20,<1" }, { name = "pytest-benchmark" }, { name = "pytest-codspeed" }, + { name = "pytest-recording" }, { name = "pytest-socket", specifier = ">=0.6.0,<1" }, - { name = "pytest-vcr" }, { name = "syrupy", specifier = ">=4,<5" }, { name = "vcrpy", specifier = ">=7.0" }, ] @@ -1153,6 +1153,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/9b/952c70bd1fae9baa58077272e7f191f377c86d812263c21b361195e125e6/pytest_codspeed-3.2.0-py3-none-any.whl", hash = "sha256:54b5c2e986d6a28e7b0af11d610ea57bd5531cec8326abe486f1b55b09d91c39", size = 15007 }, ] +[[package]] +name = "pytest-recording" +version = "0.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "vcrpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/9c/f4027c5f1693847b06d11caf4b4f6bb09f22c1581ada4663877ec166b8c6/pytest_recording-0.13.4.tar.gz", hash = "sha256:568d64b2a85992eec4ae0a419c855d5fd96782c5fb016784d86f18053792768c", size = 26576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/c2/ce34735972cc42d912173e79f200fe66530225190c06655c5632a9d88f1e/pytest_recording-0.13.4-py3-none-any.whl", hash = "sha256:ad49a434b51b1c4f78e85b1e6b74fdcc2a0a581ca16e52c798c6ace971f7f439", size = 13723 }, +] + [[package]] name = "pytest-socket" version = "0.7.0" @@ -1165,19 +1178,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/58/5d14cb5cb59409e491ebe816c47bf81423cd03098ea92281336320ae5681/pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45", size = 6754 }, ] -[[package]] -name = "pytest-vcr" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "vcrpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/60/104c619483c1a42775d3f8b27293f1ecfc0728014874d065e68cb9702d49/pytest-vcr-1.0.2.tar.gz", hash = "sha256:23ee51b75abbcc43d926272773aae4f39f93aceb75ed56852d0bf618f92e1896", size = 3810 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d3/ff520d11e6ee400602711d1ece8168dcfc5b6d8146fb7db4244a6ad6a9c3/pytest_vcr-1.0.2-py2.py3-none-any.whl", hash = "sha256:2f316e0539399bea0296e8b8401145c62b6f85e9066af7e57b6151481b0d6d9c", size = 4137 }, -] - [[package]] name = "pyyaml" version = "6.0.2"