4
\$\begingroup\$

While running unit tests (most of them integration tests) with Python I create some directories. They need to be deleted after the test or when the test fails. The execution of the cleanup code need to be guaranteed.

  • A fake or virtual filesystem (e.g. pyfakefs) or isn't a solution my situation because my unit tests call external tools (e.g. rsync) via subprocess.Popen. Also separated ramfs based filesystems not an option.
  • I prefer vanilla unittest instead of pytest.
  • The directories need to have a specific name. Random names are a problem.

My current solution is a class that holds a pathlib.Path instance as a member and deletes it from the filesystem in its __del__() method.

#!/usr/bin/env python3
import unittest
import pathlib
import shutil
import time

class SelfDestructingPath:
    def __init__(self, path):
        print('init')
        self._path = pathlib.Path(path)
        self._path.mkdir()

    def __del__(self):
        shutil.rmtree(self._path)
        print('del')

    @property
    def path(self):
        return self._path


class MyTest(unittest.TestCase):
    def test_foobar(self):
        print('test start')

        sdp = SelfDestructingPath('auto_delete')

        self.assertTrue(sdp.path.exists())

        time.sleep(5)

        self.assertTrue(False)

        print('test end')
        
if __name__ == '__main__':
    unittest.main()

The question here is if there is a more elegant solution.

I know about context managers. But in my case I don't see an advantage to them. My solution does save me one indention because I don't have to create a with block. In my application I use that feature heavily.

Another alternative would be to derive from pathlib.Path: class SelfDestructingPath(pathlib.Path). But I read somewhere that it is often not a good choice to derive from Python's own packages when you don't know what you are doing.

Any suggestions to improve this or to make it more pythonic?

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Are you on an operating system that supports a ramfs? \$\endgroup\$
    – Reinderien
    Commented Oct 18, 2022 at 12:33
  • \$\begingroup\$ Yes, but as I told pyfakefs and Co (which use ramfs) not an option for my integration tests. \$\endgroup\$
    – buhtz
    Commented Oct 18, 2022 at 12:39
  • 1
    \$\begingroup\$ Why not? External tools like rsync will happily treat a ramfs the same way. \$\endgroup\$
    – Reinderien
    Commented Oct 18, 2022 at 12:40
  • 2
    \$\begingroup\$ Please do not edit the question, especially the code, after an answer has been posted. Changing the question may cause answer invalidation. Everyone needs to be able to see what the reviewer was referring to. What to do after the question has been answered. You can create a follow up question for additional code. \$\endgroup\$
    – pacmaninbw
    Commented Oct 18, 2022 at 22:02

2 Answers 2

5
\$\begingroup\$

It appears that you have reinvented Python's tempfile.TemporaryDirectory class. Yours is slightly different, as it doesn't respect the user's $TMPDIR (or equivalent).

The implementation seems reasonable (except for its chattiness - I'd remove those print() calls).

I recommend you use Python's TemporaryDirectory; it can be used as a context manager, can be cleaned manually, or can wait until garbage collection gets around to deleting it, so you have much more flexibility than with this class.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for the thought. I forgot that point. What I need is tempfile.NamedTemporaryDirectory() which does not exist. I need to name that directory. \$\endgroup\$
    – buhtz
    Commented Oct 18, 2022 at 8:43
  • 2
    \$\begingroup\$ Do you actually need to name the temporary directory, or could you create a tempdir, and then have your named directory inside that? \$\endgroup\$ Commented Oct 18, 2022 at 11:32
  • \$\begingroup\$ Yes to your first question and now to your second. ;) \$\endgroup\$
    – buhtz
    Commented Oct 18, 2022 at 12:40
6
\$\begingroup\$

This is situational, but if it's always a single directory and there isn't too many of them, I would do creating the directory in setUp and cleaning it up in tearDown. Possibly create base class with the path as parameter if necessary. That way your test methods are not polluted by the code, that doesn't involve the test itself.

Also, whatever method you choose. Cleanup directory initially as the first thing right before recreating it. Your cleanup code is not guaranteed to execute and you may end up with test starting and the directory not being empty.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.