Skip to content

Fs fixture prevents creation of Flask test client in fixtures #1155

@ROldford

Description

@ROldford

Describe the bug
When the fs fixture is used as input into a fixture yielding a Flask test client, using the test client fixture causes the test to error with the error message "importlib.metadata.PackageNotFoundError: No package metadata was found for werkzeug"

Test stack trace:

_______________________________ ERROR at setup of test_app ________________________________

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x10bdac820>, when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: Callable[[], TResult],
        when: Literal["collect", "setup", "call", "teardown"],
        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
    ) -> CallInfo[TResult]:
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :type func: Callable[[], _pytest.runner.TResult]
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: TResult | None = func()

/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/runner.py:341: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/runner.py:242: in <lambda>
    lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/pluggy/_hooks.py:513: in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py:90: in pytest_runtest_setup
    yield from unraisable_exception_runtest_hook()
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/unraisableexception.py:70: in unraisable_exception_runtest_hook
    yield
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/logging.py:840: in pytest_runtest_setup
    yield from self._runtest_for(item, "setup")
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/logging.py:829: in _runtest_for
    yield
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/capture.py:893: in pytest_runtest_setup
    return (yield)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/threadexception.py:87: in pytest_runtest_setup
    yield from thread_exception_runtest_hook()
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook
    yield
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/runner.py:160: in pytest_runtest_setup
    item.session._setupstate.setup(item)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/runner.py:514: in setup
    col.setup()
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/python.py:1630: in setup
    self._request._fillfixtures()
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:697: in _fillfixtures
    item.funcargs[argname] = self.getfixturevalue(argname)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:532: in getfixturevalue
    fixturedef = self._get_active_fixturedef(argname)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:617: in _get_active_fixturedef
    fixturedef.execute(request=subrequest)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:1091: in execute
    result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/pluggy/_hooks.py:513: in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/setuponly.py:36: in pytest_fixture_setup
    return (yield)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:1140: in pytest_fixture_setup
    result = call_fixture_func(fixturefunc, request, kwargs)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/_pytest/fixtures.py:891: in call_fixture_func
    fixture_result = next(generator)
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/conftest.py:16: in test_client
    yield app_fixture.test_client()
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/flask/app.py:723: in test_client
    return cls(  # type: ignore
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/flask/testing.py:131: in __init__
    "HTTP_USER_AGENT": f"Werkzeug/{_get_werkzeug_version()}",
/Users/ryanoldford/Programming/Work/Berlinguette/TryingThingsOut/fakefs_importlib_metadata_bug/.venv/lib/python3.10/site-packages/flask/testing.py:103: in _get_werkzeug_version
    _werkzeug_version = importlib.metadata.version("werkzeug")
/Users/ryanoldford/.pyenv/versions/3.10.5/lib/python3.10/importlib/metadata/__init__.py:984: in version
    return distribution(distribution_name).version
/Users/ryanoldford/.pyenv/versions/3.10.5/lib/python3.10/importlib/metadata/__init__.py:957: in distribution
    return Distribution.from_name(distribution_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'importlib.metadata.Distribution'>, name = 'werkzeug'

    @classmethod
    def from_name(cls, name):
        """Return the Distribution for the given package name.
    
        :param name: The name of the distribution package to search for.
        :return: The Distribution instance (or subclass thereof) for the named
            package, if found.
        :raises PackageNotFoundError: When the named package's distribution
            metadata cannot be found.
        """
        for resolver in cls._discover_resolvers():
            dists = resolver(DistributionFinder.Context(name=name))
            dist = next(iter(dists), None)
            if dist is not None:
                return dist
        else:
>           raise PackageNotFoundError(name)
E           importlib.metadata.PackageNotFoundError: No package metadata was found for werkzeug

/Users/ryanoldford/.pyenv/versions/3.10.5/lib/python3.10/importlib/metadata/__init__.py:548: PackageNotFoundError

How To Reproduce
Import these packages: Flask 3.1.0, pyfakefs 5.8.0, pytest 8.3.5

Create the following files:
app.py

from contextlib import contextmanager
from typing import Generator
from flask import Flask
from local_types import ApiResponse


@contextmanager
def create_app() -> Generator[Flask, None, None]:
    app = Flask(__name__)
    
    @app.route("/", methods=["GET"])
    def basic_get() -> ApiResponse:
        return {"message": "Works!"}, 200
    
    yield app

local_types.py

from typing import Any, Generator, TypeVar

ApiResponse = tuple[dict[str, Any], int]


T = TypeVar("T")
YieldFixture = Generator[T, Any, Any]

conftest.py

import pytest
from local_types import YieldFixture
from flask import Flask
from flask.testing import FlaskClient
from app import create_app


@pytest.fixture(scope="function")
def app_fixture(fs) -> YieldFixture[Flask]:
    # mocking goes here
    with create_app() as _app:
        yield _app

@pytest.fixture(scope="function")
def test_client(app_fixture: Flask) -> YieldFixture[FlaskClient]:
    yield app_fixture.test_client()

test_app.py

from flask.testing import FlaskClient

def test_app(test_client: FlaskClient, fs):
    response = test_client.get("/")
    assert response.status_code == 200
    response_json = response.json
    assert isinstance(response_json, dict)
    assert response_json.get("message") == "Works!"

Run pytest and you will see the error. If you remove fs from the fixture parameters, the tests will pass normally.

Your environment
macOS-15.3.2-x86_64-i386-64bit
Python 3.10.5 (main, Apr 1 2025, 16:28:06) [Clang 16.0.0 (clang-1600.0.26.6)]
pyfakefs 5.8.0
pytest 8.3.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions