'py.test: Temporary folder for the session scope

The tmpdir fixture in py.test uses the function scope and thus isn't available in a fixture with a broader scope such as session. However, this would be useful for some cases such as setting up a temporary PostgreSQL server (which of course shouldn't be recreated for each test).

Is there any clean way to get a temporary folder for a broader scope that does not involve writing my own fixture and accessing internal APIs of py.test?



Solution 1:[1]

Since pytest release 2.8 and above the session-scoped tmpdir_factory fixture is available. See the example below from the documentation.

# contents of conftest.py
import pytest

@pytest.fixture(scope='session')
def image_file(tmpdir_factory):
    img = compute_expensive_image()
    fn = tmpdir_factory.mktemp('data').join('img.png')
    img.save(str(fn))
    return fn

# contents of test_image.py
def test_histogram(image_file):
    img = load_image(image_file)
    # compute and test histogram

Solution 2:[2]

I add a finalizer when I want to delete all temporary folders created in session.

_tmp_factory = None
@pytest.fixture(scope="session")
def tmp_factory(request, tmpdir_factory):
    global _tmp_factory
    if _tmp_factory is None:
        _tmp_factory = tmpdir_factory
        request.addfinalizer(cleanup)
    return _tmp_factory

def cleanup():
    root = _tmp_factory.getbasetemp().strpath
    print "Cleaning all temporary folders from %s" % root
    shutil.rmtree(root)

def test_deleting_temp(tmp_factory):
    root_a = tmp_factory.mktemp('A')
    root_a.join('foo.txt').write('hello world A')

    root_b = tmp_factory.mktemp('B')
    root_b.join('bar.txt').write('hello world B')

    for root, _, files in os.walk(tmp_factory.getbasetemp().strpath):
        for name in files:
            print(os.path.join(root, name))

The output should be like:

/tmp/pytest-of-agp/pytest-0/.lock
/tmp/pytest-of-agp/pytest-0/A0/foo.txt
/tmp/pytest-of-agp/pytest-0/B0/bar.txt
Cleaning all temporary folders from /tmp/pytest-of-agp/pytest-0

Solution 3:[3]

Here's another approach. It looks like pytest doesn't remove temporary directories after test runs. The following is a regular function-scoped fixture.

# conftest.py
TMPDIRS = list()

@pytest.fixture
def tmpdir_session(tmpdir):
    """A tmpdir fixture for the session scope. Persists throughout the session."""
    if not TMPDIRS:
        TMPDIRS.append(tmpdir)
    return TMPDIRS[0]

And to have persistent temporary directories across modules instead of the whole pytest session:

# conftest.py
TMPDIRS = dict()

@pytest.fixture
def tmpdir_module(request, tmpdir):
    """A tmpdir fixture for the module scope. Persists throughout the  module."""
    return TMPDIRS.setdefault(request.module.__name__, tmpdir)

Edit: Here's another solution that doesn't involve global variables. pytest 1.8.0 introduced a tmpdir_factory fixture that we can use:

@pytest.fixture(scope='module')
def tmpdir_module(request, tmpdir_factory):
    """A tmpdir fixture for the module scope. Persists throughout the module."""
    return tmpdir_factory.mktemp(request.module.__name__)


@pytest.fixture(scope='session')
def tmpdir_session(request, tmpdir_factory):
    """A tmpdir fixture for the session scope. Persists throughout the pytest session."""
    return tmpdir_factory.mktemp(request.session.name)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mehraban
Solution 2 asterio gonzalez
Solution 3