tmpdir.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. """ support for providing temporary directories to test functions. """
  2. from __future__ import absolute_import, division, print_function
  3. import re
  4. import pytest
  5. import py
  6. from _pytest.monkeypatch import MonkeyPatch
  7. class TempdirFactory(object):
  8. """Factory for temporary directories under the common base temp directory.
  9. The base directory can be configured using the ``--basetemp`` option.
  10. """
  11. def __init__(self, config):
  12. self.config = config
  13. self.trace = config.trace.get("tmpdir")
  14. def ensuretemp(self, string, dir=1):
  15. """ (deprecated) return temporary directory path with
  16. the given string as the trailing part. It is usually
  17. better to use the 'tmpdir' function argument which
  18. provides an empty unique-per-test-invocation directory
  19. and is guaranteed to be empty.
  20. """
  21. # py.log._apiwarn(">1.1", "use tmpdir function argument")
  22. return self.getbasetemp().ensure(string, dir=dir)
  23. def mktemp(self, basename, numbered=True):
  24. """Create a subdirectory of the base temporary directory and return it.
  25. If ``numbered``, ensure the directory is unique by adding a number
  26. prefix greater than any existing one.
  27. """
  28. basetemp = self.getbasetemp()
  29. if not numbered:
  30. p = basetemp.mkdir(basename)
  31. else:
  32. p = py.path.local.make_numbered_dir(
  33. prefix=basename, keep=0, rootdir=basetemp, lock_timeout=None
  34. )
  35. self.trace("mktemp", p)
  36. return p
  37. def getbasetemp(self):
  38. """ return base temporary directory. """
  39. try:
  40. return self._basetemp
  41. except AttributeError:
  42. basetemp = self.config.option.basetemp
  43. if basetemp:
  44. basetemp = py.path.local(basetemp)
  45. if basetemp.check():
  46. basetemp.remove()
  47. basetemp.mkdir()
  48. else:
  49. temproot = py.path.local.get_temproot()
  50. user = get_user()
  51. if user:
  52. # use a sub-directory in the temproot to speed-up
  53. # make_numbered_dir() call
  54. rootdir = temproot.join("pytest-of-%s" % user)
  55. else:
  56. rootdir = temproot
  57. rootdir.ensure(dir=1)
  58. basetemp = py.path.local.make_numbered_dir(
  59. prefix="pytest-", rootdir=rootdir
  60. )
  61. self._basetemp = t = basetemp.realpath()
  62. self.trace("new basetemp", t)
  63. return t
  64. def finish(self):
  65. self.trace("finish")
  66. def get_user():
  67. """Return the current user name, or None if getuser() does not work
  68. in the current environment (see #1010).
  69. """
  70. import getpass
  71. try:
  72. return getpass.getuser()
  73. except (ImportError, KeyError):
  74. return None
  75. # backward compatibility
  76. TempdirHandler = TempdirFactory
  77. def pytest_configure(config):
  78. """Create a TempdirFactory and attach it to the config object.
  79. This is to comply with existing plugins which expect the handler to be
  80. available at pytest_configure time, but ideally should be moved entirely
  81. to the tmpdir_factory session fixture.
  82. """
  83. mp = MonkeyPatch()
  84. t = TempdirFactory(config)
  85. config._cleanup.extend([mp.undo, t.finish])
  86. mp.setattr(config, "_tmpdirhandler", t, raising=False)
  87. mp.setattr(pytest, "ensuretemp", t.ensuretemp, raising=False)
  88. @pytest.fixture(scope="session")
  89. def tmpdir_factory(request):
  90. """Return a TempdirFactory instance for the test session.
  91. """
  92. return request.config._tmpdirhandler
  93. @pytest.fixture
  94. def tmpdir(request, tmpdir_factory):
  95. """Return a temporary directory path object
  96. which is unique to each test function invocation,
  97. created as a sub directory of the base temporary
  98. directory. The returned object is a `py.path.local`_
  99. path object.
  100. .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
  101. """
  102. name = request.node.name
  103. name = re.sub(r"[\W]", "_", name)
  104. MAXVAL = 30
  105. if len(name) > MAXVAL:
  106. name = name[:MAXVAL]
  107. x = tmpdir_factory.mktemp(name, numbered=True)
  108. return x