kiwicom/pytest-recording

[FEATURE] "Lazy load" VCR to reduce plugin overhead

herbierand opened this issue · 2 comments

Is your feature request related to a problem? Please describe.

The plugin currently imports vcr regardless of whether any vcr-marked tests are collected by pytest. This import can be slow (on the order of hundreds of milliseconds) in environments where multiple HTTP client libraries supported by VCR.py are installed.

Describe the solution you'd like

Implement "lazy importing" of vcr so that the cost of importing it is incurred only when necessary. For example, move the from ._vcr import use_cassette import inside the vcr fixture in plugin.py:

@pytest.fixture(autouse=True)  # type: ignore
def vcr(
    request: SubRequest,
    vcr_markers: List[Mark],
    vcr_cassette_dir: str,
    record_mode: str,
    disable_recording: bool,
    pytestconfig: Config,
) -> Iterator[Optional["Cassette"]]:
    """Install a cassette if a test is marked with `pytest.mark.vcr`."""
    if disable_recording:
        yield None
    elif vcr_markers:

        # IMPORT HERE
        from ._vcr import use_cassette

        config = request.getfixturevalue("vcr_config")
        default_cassette = request.getfixturevalue("default_cassette_name")
        with use_cassette(
            default_cassette,
            vcr_cassette_dir,
            record_mode,
            vcr_markers,
            config,
            pytestconfig,
        ) as cassette:
            yield cassette
    else:
        yield None

Describe alternatives you've considered

I considered disabling the plugin by default and re-enabling it after a vcr-marked test is collected. However, this approach is not feasible because the plugin relies on hooks that must execute before test collection.

Additional context

I measured the overhead of importing vcr in an environment with all the VCR.py compatible http client libraries installed (https://vcrpy.readthedocs.io/en/latest/installation.html#compatibility).

In a Python REPL (Macbook Pro M2):

>>> import time
>>> start = time.time(); import vcr; elapsed = time.time() - start
>>> elapsed
0.2606179714202881

Without this cost, the plugin could be enabled by default with minimal overhead.

If deemed appropriate, I would be willing to contribute a PR.

Thanks! Sounds good to me!

Solved in #146