Source code for zope.testing.setupstack
##############################################################################
#
# Copyright (c) 2005 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Stack-based test `doctest` ``setUp`` and `tearDown`
See :doc:`../setupstack`
"""
import os
import stat
import tempfile
import unittest
key = '__' + __name__
[docs]
def globs(test):
"""
Return the globals for *test*, which can be a `doctest`
or a regular `unittest.TestCase`.
"""
try:
return test.globs
except AttributeError:
return test.__dict__
[docs]
def register(test, function, *args, **kw):
"""
Add a clean up *function* to be called with
*args* and *kw* when *test* is torn down.
Clean up functions are called in the reverse order
of registration.
"""
tglobs = globs(test)
stack = tglobs.get(key)
if stack is None:
stack = tglobs[key] = []
stack.append((function, args, kw))
[docs]
def tearDown(test):
"""
Call all the clean up functions registered for *test*,
in the reverse of their registration order.
"""
tglobs = globs(test)
stack = tglobs.get(key)
while stack:
f, p, k = stack.pop()
f(*p, **k)
[docs]
def setUpDirectory(test):
"""
Create and change to a temporary directory, and `register`
`rmtree` to clean it up. Returns to the starting directory
when finished.
"""
tmp = tempfile.mkdtemp()
register(test, rmtree, tmp)
here = os.getcwd()
register(test, os.chdir, here)
os.chdir(tmp)
[docs]
def rmtree(path):
"""
Remove all the files and directories
found in *path*.
Intended to be used as a clean up function with *register*.
"""
for path, dirs, files in os.walk(path, False):
for fname in files:
fname = os.path.join(path, fname)
if not os.path.islink(fname):
os.chmod(fname, stat.S_IWUSR)
os.remove(fname)
for dname in dirs:
dname = os.path.join(path, dname)
os.rmdir(dname)
os.rmdir(path)
[docs]
def context_manager(test, manager):
"""
A stack-based version of the **with** statement.
The context manager *manager* is entered when this
method is called, and it is exited when the *test*
is torn down.
"""
result = manager.__enter__()
register(test, manager.__exit__, None, None, None)
return result
def _get_mock():
# A hook for the setupstack.txt doctest :
from unittest import mock as mock_module
return mock_module
[docs]
def mock(test, *args, **kw):
"""
Patch an object by calling `unittest.mock.patch`
with the *args* and *kw* given, and returns the result.
This will be torn down when the *test* is torn down.
"""
mock_module = _get_mock()
return context_manager(test, mock_module.patch(*args, **kw))
[docs]
class TestCase(unittest.TestCase):
"""
A ``TestCase`` base class that overrides `tearDown`
to use the function from this module.
In addition, it provides other methods using the
functions in this module:
- `register`
- `setUpDirectory`
- `context_manager`
- `mock`
"""
tearDown = tearDown
register = register
setUpDirectory = setUpDirectory
context_manager = context_manager
mock = mock