flask/tests/conftest.py

199 lines
5.2 KiB
Python
Raw Normal View History

2014-09-04 02:50:54 +08:00
import os
2014-09-02 01:06:32 +08:00
import pkgutil
2018-01-11 05:53:45 +08:00
import sys
2014-09-02 01:06:32 +08:00
import textwrap
2018-01-11 05:53:45 +08:00
import pytest
from _pytest import monkeypatch
import flask
from flask import Flask as _Flask
@pytest.fixture(scope="session", autouse=True)
2018-01-11 05:53:45 +08:00
def _standard_os_environ():
"""Set up ``os.environ`` at the start of the test session to have
standard values. Returns a list of operations that is used by
:func:`._reset_os_environ` after each test.
"""
mp = monkeypatch.MonkeyPatch()
out = (
2022-06-18 00:26:26 +08:00
(os.environ, "FLASK_ENV_FILE", monkeypatch.notset),
(os.environ, "FLASK_APP", monkeypatch.notset),
(os.environ, "FLASK_ENV", monkeypatch.notset),
(os.environ, "FLASK_DEBUG", monkeypatch.notset),
(os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset),
(os.environ, "WERKZEUG_RUN_MAIN", monkeypatch.notset),
2018-01-11 05:53:45 +08:00
)
for _, key, value in out:
if value is monkeypatch.notset:
mp.delenv(key, False)
else:
mp.setenv(key, value)
yield out
mp.undo()
@pytest.fixture(autouse=True)
def _reset_os_environ(monkeypatch, _standard_os_environ):
"""Reset ``os.environ`` to the standard environ after each test,
in case a test changed something without cleaning up.
"""
monkeypatch._setitem.extend(_standard_os_environ)
class Flask(_Flask):
testing = True
secret_key = "test key"
@pytest.fixture
def app():
app = Flask("flask_test", root_path=os.path.dirname(__file__))
return app
@pytest.fixture
def app_ctx(app):
with app.app_context() as ctx:
yield ctx
@pytest.fixture
def req_ctx(app):
with app.test_request_context() as ctx:
yield ctx
@pytest.fixture
def client(app):
return app.test_client()
2014-09-02 01:06:32 +08:00
2014-09-04 20:37:48 +08:00
@pytest.fixture
def test_apps(monkeypatch):
monkeypatch.syspath_prepend(os.path.join(os.path.dirname(__file__), "test_apps"))
original_modules = set(sys.modules.keys())
yield
# Remove any imports cached during the test. Otherwise "import app"
# will work in the next test even though it's no longer on the path.
for key in sys.modules.keys() - original_modules:
sys.modules.pop(key)
2014-09-04 02:50:54 +08:00
2014-09-04 02:50:54 +08:00
@pytest.fixture(autouse=True)
def leak_detector():
yield
# make sure we're not leaking a request context since we are
# testing flask internally in debug mode in a few cases
leaks = []
while flask._request_ctx_stack.top is not None:
leaks.append(flask._request_ctx_stack.pop())
assert leaks == []
2014-09-04 02:50:54 +08:00
2014-09-02 01:06:32 +08:00
@pytest.fixture(params=(True, False))
def limit_loader(request, monkeypatch):
"""Patch pkgutil.get_loader to give loader without get_filename or archive.
This provides for tests where a system has custom loaders, e.g. Google App
Engine's HardenedModulesHook, which have neither the `get_filename` method
nor the `archive` attribute.
This fixture will run the testcase twice, once with and once without the
limitation/mock.
"""
if not request.param:
return
2020-04-05 00:43:06 +08:00
class LimitedLoader:
2014-09-02 01:06:32 +08:00
def __init__(self, loader):
self.loader = loader
def __getattr__(self, name):
2020-04-05 02:39:03 +08:00
if name in {"archive", "get_filename"}:
raise AttributeError(f"Mocking a loader which does not have {name!r}.")
2014-09-02 01:06:32 +08:00
return getattr(self.loader, name)
old_get_loader = pkgutil.get_loader
def get_loader(*args, **kwargs):
return LimitedLoader(old_get_loader(*args, **kwargs))
monkeypatch.setattr(pkgutil, "get_loader", get_loader)
2014-09-02 01:06:32 +08:00
@pytest.fixture
2014-09-04 21:22:57 +08:00
def modules_tmpdir(tmpdir, monkeypatch):
"""A tmpdir added to sys.path."""
rv = tmpdir.mkdir("modules_tmpdir")
2014-09-02 01:06:32 +08:00
monkeypatch.syspath_prepend(str(rv))
return rv
@pytest.fixture
2014-09-04 21:22:57 +08:00
def modules_tmpdir_prefix(modules_tmpdir, monkeypatch):
monkeypatch.setattr(sys, "prefix", str(modules_tmpdir))
2014-09-04 21:22:57 +08:00
return modules_tmpdir
2014-09-02 01:06:32 +08:00
@pytest.fixture
2014-09-04 21:22:57 +08:00
def site_packages(modules_tmpdir, monkeypatch):
"""Create a fake site-packages."""
rv = (
modules_tmpdir.mkdir("lib")
2020-04-05 02:39:03 +08:00
.mkdir(f"python{sys.version_info.major}.{sys.version_info.minor}")
.mkdir("site-packages")
)
2014-09-02 01:06:32 +08:00
monkeypatch.syspath_prepend(str(rv))
return rv
@pytest.fixture
2014-09-04 21:22:57 +08:00
def install_egg(modules_tmpdir, monkeypatch):
"""Generate egg from package name inside base and put the egg into
sys.path."""
2014-09-04 21:22:57 +08:00
def inner(name, base=modules_tmpdir):
2014-09-02 01:06:32 +08:00
base.join(name).ensure_dir()
base.join(name).join("__init__.py").ensure()
2014-09-02 01:06:32 +08:00
egg_setup = base.join("setup.py")
egg_setup.write(
textwrap.dedent(
2020-04-05 02:39:03 +08:00
f"""
from setuptools import setup
setup(
name="{name}",
version="1.0",
packages=["site_egg"],
zip_safe=True,
)
2020-04-05 02:39:03 +08:00
"""
)
)
2014-09-02 01:06:32 +08:00
import subprocess
2014-09-02 01:06:32 +08:00
subprocess.check_call(
[sys.executable, "setup.py", "bdist_egg"], cwd=str(modules_tmpdir)
2014-09-02 01:06:32 +08:00
)
2020-04-05 00:43:06 +08:00
(egg_path,) = modules_tmpdir.join("dist/").listdir()
2014-09-02 01:06:32 +08:00
monkeypatch.syspath_prepend(str(egg_path))
return egg_path
2014-09-02 01:06:32 +08:00
return inner
@pytest.fixture
def purge_module(request):
def inner(name):
request.addfinalizer(lambda: sys.modules.pop(name, None))
2014-09-02 01:06:32 +08:00
return inner