Use a subdirectory in the TEMP directory to speed up tmpdir creation
Fix #105
This commit is contained in:
		
							parent
							
								
									8f4f2c665d
								
							
						
					
					
						commit
						0f52856f99
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue