Skip to content

pytest_pyodide.hook make collection very slow even if @run_in_pyodide is never used #178

Description

@antocuni

TL;DR: if you have a moderately-sized test suite, hook.py:pytest_pycollect_makemodule make collection noticeably slower.

I bumbed into issue when working on SPy.
SPy has 65 test files and ~5k total test lines:

❯ cloc --vcs=git spy/tests
      65 text files.
      65 unique files.                              
       3 files ignored.

github.com/AlDanial/cloc v 1.98  T=0.13 s (509.0 files/s, 95704.2 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Python                          65           1813           4653           5756

I have pytest-pyodide installed in my venv, and collection takes ~3s:

❯ pytest -q --collect-only
[...]
1203 tests collected in 3.2s

If I manually comment out this function, I shave more that 1 second:

def pytest_pycollect_makemodule(module_path: Path, parent: Collector) -> None:
source = module_path.read_bytes()
strfn = str(module_path)
tree = ast.parse(source, filename=strfn)
ORIGINAL_MODULE_ASTS[strfn] = tree
tree2 = deepcopy(tree)
rewrite_asserts(tree2, source, strfn, REWRITE_CONFIG)
REWRITTEN_MODULE_ASTS[strfn] = tree2
orig_pytest_pycollect_makemodule(module_path, parent)

❯ pytest -q --collect-only
[...]
1203 tests collected in 1.29s

I don't fully understand what pytest_collect_makemodule does, but it seems related to @run_in_pyodide.
Note that my tests use this functionality only in one test file:

❯ rg 'run_in_pyodide' spy/tests
spy/tests/test_llwasm.py
2:from pytest_pyodide import run_in_pyodide  # type: ignore
49:            self.run_in_pyodide_maybe = run_in_pyodide
53:            self.run_in_pyodide_maybe = lambda fn: fn
63:        @self.run_in_pyodide_maybe
85:        @self.run_in_pyodide_maybe
105:        @self.run_in_pyodide_maybe
124:        @self.run_in_pyodide_maybe
148:        @self.run_in_pyodide_maybe
173:        @self.run_in_pyodide_maybe
207:        @self.run_in_pyodide_maybe

I tried to do this hack and it seems to help a lot, although again I'm not really sure to understand what it does:

def pytest_pycollect_makemodule(module_path: Path, parent: Collector) -> None:
    source = module_path.read_bytes()
    if b"run_in_pyodide" in source:
        strfn = str(module_path)
        tree = ast.parse(source, filename=strfn)
        ORIGINAL_MODULE_ASTS[strfn] = tree
        tree2 = deepcopy(tree)
        rewrite_asserts(tree2, source, strfn, REWRITE_CONFIG)
        REWRITTEN_MODULE_ASTS[strfn] = tree2
    orig_pytest_pycollect_makemodule(module_path, parent)
❯ pytest -q --collect-only
[...]
1203 tests collected in 1.34s

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions