Use a subdirectory in the TEMP directory to speed up tmpdir creation

Fix #105
This commit is contained in:
Bruno Oliveira 2015-07-15 20:03:58 -03:00
parent 8f4f2c665d
commit 0f52856f99
7 changed files with 109 additions and 33 deletions

View File

@ -4,6 +4,13 @@
- fix issue768: docstrings found in python modules were not setting up session
fixtures. Thanks Jason R. Coombs for reporting and Bruno Oliveira for the PR.
- added `tmpdir_factory`, a session-scoped fixture that can be used to create
directories under the base temporary directory. Previously this object was
installed as a `_tmpdirhandler` attribute of the `config` object, but now it
is part of the official API and using `config._tmpdirhandler` is
deprecated.
Thanks Bruno Oliveira for the PR.
- fix issue 808: pytest's internal assertion rewrite hook now implements the
optional PEP302 get_data API so tests can access data files next to them.
Thanks xmo-odoo for request and example and Bruno Oliveira for

View File

@ -309,16 +309,18 @@ class HookRecorder:
self.calls[:] = []
def pytest_funcarg__linecomp(request):
@pytest.fixture
def linecomp(request):
return LineComp()
def pytest_funcarg__LineMatcher(request):
return LineMatcher
def pytest_funcarg__testdir(request):
tmptestdir = Testdir(request)
return tmptestdir
@pytest.fixture
def testdir(request, tmpdir_factory):
return Testdir(request, tmpdir_factory)
rex_outcome = re.compile("(\d+) (\w+)")
@ -388,10 +390,10 @@ class Testdir:
"""
def __init__(self, request):
def __init__(self, request, tmpdir_factory):
self.request = request
# XXX remove duplication with tmpdir plugin
basetmp = request.config._tmpdirhandler.ensuretemp("testdir")
basetmp = tmpdir_factory.ensuretemp("testdir")
name = request.function.__name__
for i in range(100):
try:

View File

@ -6,7 +6,12 @@ import py
from _pytest.monkeypatch import monkeypatch
class TempdirHandler:
class TempdirFactory:
"""Factory for temporary directories under the common base temp directory.
The base directory can be configured using the ``--basetemp`` option.
"""
def __init__(self, config):
self.config = config
self.trace = config.trace.get("tmpdir")
@ -22,6 +27,10 @@ class TempdirHandler:
return self.getbasetemp().ensure(string, dir=dir)
def mktemp(self, basename, numbered=True):
"""Create a subdirectory of the base temporary directory and return it.
If ``numbered``, ensure the directory is unique by adding a number
prefix greater than any existing one.
"""
basetemp = self.getbasetemp()
if not numbered:
p = basetemp.mkdir(basename)
@ -51,15 +60,33 @@ class TempdirHandler:
def finish(self):
self.trace("finish")
# backward compatibility
TempdirHandler = TempdirFactory
def pytest_configure(config):
"""Create a TempdirFactory and attach it to the config object.
This is to comply with existing plugins which expect the handler to be
available at pytest_configure time, but ideally should be moved entirely
to the tmpdir_factory session fixture.
"""
mp = monkeypatch()
t = TempdirHandler(config)
t = TempdirFactory(config)
config._cleanup.extend([mp.undo, t.finish])
mp.setattr(config, '_tmpdirhandler', t, raising=False)
mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False)
@pytest.fixture(scope='session')
def tmpdir_factory(request):
"""Return a TempdirFactory instance for the test session.
"""
return request.config._tmpdirhandler
@pytest.fixture
def tmpdir(request):
def tmpdir(request, tmpdir_factory):
"""return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
@ -71,5 +98,5 @@ def tmpdir(request):
MAXVAL = 30
if len(name) > MAXVAL:
name = name[:MAXVAL]
x = request.config._tmpdirhandler.mktemp(name, numbered=True)
x = tmpdir_factory.mktemp(name, numbered=True)
return x

View File

@ -5,10 +5,10 @@
Temporary directories and files
================================================
The 'tmpdir' test function argument
-----------------------------------
The 'tmpdir' fixture
--------------------
You can use the ``tmpdir`` function argument which will
You can use the ``tmpdir`` fixture which will
provide a temporary directory unique to the test invocation,
created in the `base temporary directory`_.
@ -51,6 +51,44 @@ Running this would result in a passed test except for the last
test_tmpdir.py:7: AssertionError
======= 1 failed in 0.12 seconds ========
The 'tmpdir_factory' fixture
----------------------------
.. versionadded:: 2.8
The ``tmpdir_factory`` is a session-scoped fixture which can be used
to create arbitrary temporary directories from any other fixture or test.
For example, suppose your test suite needs a large image on disk, which is
generated procedurally. Instead of computing the same image for each test
that uses it into its own ``tmpdir``, you can generate it once per-session
to save time:
.. code-block:: python
# 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
``tmpdir_factory`` instances have the following methods:
.. currentmodule:: _pytest.tmpdir
.. automethod:: TempdirFactory.mktemp
.. automethod:: TempdirFactory.getbasetemp
.. _`base temporary directory`:
The default base temporary directory

View File

@ -555,7 +555,8 @@ class TestRequestBasic:
pass
def test_function(request, farg):
assert set(get_public_names(request.fixturenames)) == \
set(["tmpdir", "sarg", "arg1", "request", "farg"])
set(["tmpdir", "sarg", "arg1", "request", "farg",
"tmpdir_factory"])
""")
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)

View File

@ -4,9 +4,9 @@ from _pytest.config import PytestPluginManager
@pytest.fixture(scope="module", params=["global", "inpackage"])
def basedir(request):
def basedir(request, tmpdir_factory):
from _pytest.tmpdir import tmpdir
tmpdir = tmpdir(request)
tmpdir = tmpdir(request, tmpdir_factory)
tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3")
tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5")
if request.param == "inpackage":

View File

@ -1,7 +1,7 @@
import py
import pytest
from _pytest.tmpdir import tmpdir, TempdirHandler
from _pytest.tmpdir import tmpdir, TempdirFactory
def test_funcarg(testdir):
testdir.makepyfile("""
@ -13,16 +13,15 @@ def test_funcarg(testdir):
reprec = testdir.inline_run()
calls = reprec.getcalls("pytest_runtest_setup")
item = calls[0].item
# pytest_unconfigure has deleted the TempdirHandler already
config = item.config
config._tmpdirhandler = TempdirHandler(config)
tmpdirhandler = TempdirFactory(config)
item._initrequest()
p = tmpdir(item._request)
p = tmpdir(item._request, tmpdirhandler)
assert p.check()
bn = p.basename.strip("0123456789")
assert bn.endswith("test_func_a_")
item.name = "qwe/\\abc"
p = tmpdir(item._request)
p = tmpdir(item._request, tmpdirhandler)
assert p.check()
bn = p.basename.strip("0123456789")
assert bn == "qwe__abc"
@ -38,7 +37,7 @@ class TestTempdirHandler:
def test_mktemp(self, testdir):
config = testdir.parseconfig()
config.option.basetemp = testdir.mkdir("hello")
t = TempdirHandler(config)
t = TempdirFactory(config)
tmp = t.mktemp("world")
assert tmp.relto(t.getbasetemp()) == "world0"
tmp = t.mktemp("this")
@ -49,17 +48,19 @@ class TestTempdirHandler:
class TestConfigTmpdir:
def test_getbasetemp_custom_removes_old(self, testdir):
p = testdir.tmpdir.join("xyz")
config = testdir.parseconfigure("--basetemp=xyz")
b = config._tmpdirhandler.getbasetemp()
assert b == p
h = b.ensure("hello")
config._tmpdirhandler.getbasetemp()
assert h.check()
config = testdir.parseconfigure("--basetemp=xyz")
b2 = config._tmpdirhandler.getbasetemp()
assert b2.check()
assert not h.check()
mytemp = testdir.tmpdir.join("xyz")
p = testdir.makepyfile("""
def test_1(tmpdir):
pass
""")
testdir.runpytest(p, '--basetemp=%s' % mytemp)
mytemp.check()
mytemp.ensure("hello")
testdir.runpytest(p, '--basetemp=%s' % mytemp)
mytemp.check()
assert not mytemp.join("hello").check()
def test_basetemp(testdir):
mytemp = testdir.tmpdir.mkdir("mytemp")