[tor-commits] [stem/master] Replace mkdtemp() with TemporaryDirectory()

atagar at torproject.org atagar at torproject.org
Sun Jan 5 21:39:28 UTC 2020


commit 85c81eea7fdad016384e734791cccf0e97132fc1
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Jan 4 15:25:14 2020 -0800

    Replace mkdtemp() with TemporaryDirectory()
    
    Minor python 3.x simplification now that we can use this builtin.
---
 stem/descriptor/collector.py     |  13 ++---
 stem/manual.py                   |   9 ++--
 test/integ/control/controller.py | 105 +++++++++++++++++++--------------------
 test/integ/process.py            |  31 ++++--------
 test/integ/util/system.py        |  11 ++--
 test/unit/manual.py              |  16 +++---
 6 files changed, 82 insertions(+), 103 deletions(-)

diff --git a/stem/descriptor/collector.py b/stem/descriptor/collector.py
index c2bf8179..7aeb298b 100644
--- a/stem/descriptor/collector.py
+++ b/stem/descriptor/collector.py
@@ -55,7 +55,6 @@ import hashlib
 import json
 import os
 import re
-import shutil
 import tempfile
 import time
 
@@ -264,15 +263,9 @@ class File(object):
       if self._downloaded_to and os.path.exists(self._downloaded_to):
         directory = os.path.dirname(self._downloaded_to)
       else:
-        # TODO: The following can be replaced with simpler usage of
-        # tempfile.TemporaryDirectory when we drop python 2.x support.
-
-        tmp_directory = tempfile.mkdtemp()
-
-        for desc in self.read(tmp_directory, descriptor_type, start, end, document_handler, timeout, retries):
-          yield desc
-
-        shutil.rmtree(tmp_directory)
+        with tempfile.TemporaryDirectory() as tmp_directory:
+          for desc in self.read(tmp_directory, descriptor_type, start, end, document_handler, timeout, retries):
+            yield desc
 
         return
 
diff --git a/stem/manual.py b/stem/manual.py
index fe49e3de..c1b4bd8f 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -288,11 +288,10 @@ def download_man_page(path = None, file_handle = None, url = GITWEB_MANUAL_URL,
   elif not stem.util.system.is_available('a2x'):
     raise IOError('We require a2x from asciidoc to provide a man page')
 
-  dirpath = tempfile.mkdtemp()
-  asciidoc_path = os.path.join(dirpath, 'tor.1.txt')
-  manual_path = os.path.join(dirpath, 'tor.1')
+  with tempfile.TemporaryDirectory() as dirpath:
+    asciidoc_path = os.path.join(dirpath, 'tor.1.txt')
+    manual_path = os.path.join(dirpath, 'tor.1')
 
-  try:
     try:
       with open(asciidoc_path, 'wb') as asciidoc_file:
         request = urllib.request.urlopen(url, timeout = timeout)
@@ -325,8 +324,6 @@ def download_man_page(path = None, file_handle = None, url = GITWEB_MANUAL_URL,
       with open(manual_path, 'rb') as manual_file:
         shutil.copyfileobj(manual_file, file_handle)
         file_handle.flush()
-  finally:
-    shutil.rmtree(dirpath)
 
 
 class Manual(object):
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index f87c0817..257d9fbc 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -754,71 +754,70 @@ class TestController(unittest.TestCase):
     """
 
     runner = test.runner.get_runner()
-    tmpdir = tempfile.mkdtemp()
 
-    with runner.get_tor_controller() as controller:
-      try:
-        # successfully set a single option
-        connlimit = int(controller.get_conf('ConnLimit'))
-        controller.set_conf('connlimit', str(connlimit - 1))
-        self.assertEqual(connlimit - 1, int(controller.get_conf('ConnLimit')))
-
-        # successfully set a single list option
-        exit_policy = ['accept *:7777', 'reject *:*']
-        controller.set_conf('ExitPolicy', exit_policy)
-        self.assertEqual(exit_policy, controller.get_conf('ExitPolicy', multiple = True))
+    with tempfile.TemporaryDirectory() as tmpdir:
 
-        # fail to set a single option
+      with runner.get_tor_controller() as controller:
         try:
-          controller.set_conf('invalidkeyboo', 'abcde')
-          self.fail()
-        except stem.InvalidArguments as exc:
-          self.assertEqual(['invalidkeyboo'], exc.arguments)
+          # successfully set a single option
+          connlimit = int(controller.get_conf('ConnLimit'))
+          controller.set_conf('connlimit', str(connlimit - 1))
+          self.assertEqual(connlimit - 1, int(controller.get_conf('ConnLimit')))
 
-        # resets configuration parameters
-        controller.reset_conf('ConnLimit', 'ExitPolicy')
-        self.assertEqual(connlimit, int(controller.get_conf('ConnLimit')))
-        self.assertEqual(None, controller.get_conf('ExitPolicy'))
+          # successfully set a single list option
+          exit_policy = ['accept *:7777', 'reject *:*']
+          controller.set_conf('ExitPolicy', exit_policy)
+          self.assertEqual(exit_policy, controller.get_conf('ExitPolicy', multiple = True))
 
-        # successfully sets multiple config options
-        controller.set_options({
-          'connlimit': str(connlimit - 2),
-          'contactinfo': 'stem at testing',
-        })
+          # fail to set a single option
+          try:
+            controller.set_conf('invalidkeyboo', 'abcde')
+            self.fail()
+          except stem.InvalidArguments as exc:
+            self.assertEqual(['invalidkeyboo'], exc.arguments)
 
-        self.assertEqual(connlimit - 2, int(controller.get_conf('ConnLimit')))
-        self.assertEqual('stem at testing', controller.get_conf('contactinfo'))
+          # resets configuration parameters
+          controller.reset_conf('ConnLimit', 'ExitPolicy')
+          self.assertEqual(connlimit, int(controller.get_conf('ConnLimit')))
+          self.assertEqual(None, controller.get_conf('ExitPolicy'))
 
-        # fail to set multiple config options
-        try:
+          # successfully sets multiple config options
           controller.set_options({
+            'connlimit': str(connlimit - 2),
             'contactinfo': 'stem at testing',
-            'bombay': 'vadapav',
           })
-          self.fail()
-        except stem.InvalidArguments as exc:
-          self.assertEqual(['bombay'], exc.arguments)
 
-        # context-sensitive keys (the only retched things for which order matters)
-        controller.set_options((
-          ('HiddenServiceDir', tmpdir),
-          ('HiddenServicePort', '17234 127.0.0.1:17235'),
-        ))
+          self.assertEqual(connlimit - 2, int(controller.get_conf('ConnLimit')))
+          self.assertEqual('stem at testing', controller.get_conf('contactinfo'))
 
-        self.assertEqual(tmpdir, controller.get_conf('HiddenServiceDir'))
-        self.assertEqual('17234 127.0.0.1:17235', controller.get_conf('HiddenServicePort'))
-      finally:
-        # reverts configuration changes
-
-        controller.set_options((
-          ('ExitPolicy', 'reject *:*'),
-          ('ConnLimit', None),
-          ('ContactInfo', None),
-          ('HiddenServiceDir', None),
-          ('HiddenServicePort', None),
-        ), reset = True)
-
-        shutil.rmtree(tmpdir)
+          # fail to set multiple config options
+          try:
+            controller.set_options({
+              'contactinfo': 'stem at testing',
+              'bombay': 'vadapav',
+            })
+            self.fail()
+          except stem.InvalidArguments as exc:
+            self.assertEqual(['bombay'], exc.arguments)
+
+          # context-sensitive keys (the only retched things for which order matters)
+          controller.set_options((
+            ('HiddenServiceDir', tmpdir),
+            ('HiddenServicePort', '17234 127.0.0.1:17235'),
+          ))
+
+          self.assertEqual(tmpdir, controller.get_conf('HiddenServiceDir'))
+          self.assertEqual('17234 127.0.0.1:17235', controller.get_conf('HiddenServicePort'))
+        finally:
+          # reverts configuration changes
+
+          controller.set_options((
+            ('ExitPolicy', 'reject *:*'),
+            ('ConnLimit', None),
+            ('ContactInfo', None),
+            ('HiddenServiceDir', None),
+            ('HiddenServicePort', None),
+          ), reset = True)
 
   @test.require.controller
   def test_set_conf_for_usebridges(self):
diff --git a/test/integ/process.py b/test/integ/process.py
index e7d2cae3..e40c501a 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -9,7 +9,6 @@ import hashlib
 import os
 import random
 import re
-import shutil
 import subprocess
 import tempfile
 import threading
@@ -53,18 +52,8 @@ def random_port():
 
 
 @contextmanager
-def tmp_directory():
-  tmp_dir = tempfile.mkdtemp()
-
-  try:
-    yield tmp_dir
-  finally:
-    shutil.rmtree(tmp_dir)
-
-
- at contextmanager
 def torrc():
-  with tmp_directory() as data_directory:
+  with tempfile.TemporaryDirectory() as data_directory:
     torrc_path = os.path.join(data_directory, 'torrc')
 
     with open(torrc_path, 'w') as torrc_file:
@@ -228,7 +217,7 @@ class TestProcess(unittest.TestCase):
     output = run_tor(tor_cmd, '--list-fingerprint', with_torrc = True, expect_failure = True)
     assert_in("Clients don't have long-term identity keys. Exiting.", output, 'Should fail to start due to lacking an ORPort')
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       torrc_path = os.path.join(data_directory, 'torrc')
 
       with open(torrc_path, 'w') as torrc_file:
@@ -324,7 +313,7 @@ class TestProcess(unittest.TestCase):
     if test.tor_version() < stem.version.Requirement.TORRC_VIA_STDIN:
       skip('(requires %s)' % stem.version.Requirement.TORRC_VIA_STDIN)
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       torrc = BASIC_RELAY_TORRC % data_directory
       output = run_tor(tor_cmd, '-f', '-', '--dump-config', 'short', stdin = torrc)
       assert_equal(sorted(torrc.splitlines()), sorted(output.splitlines()))
@@ -382,7 +371,7 @@ class TestProcess(unittest.TestCase):
     it isn't.
     """
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       # Tries running tor in another thread with the given timeout argument. This
       # issues an invalid torrc so we terminate right away if we get to the point
       # of actually invoking tor.
@@ -435,7 +424,7 @@ class TestProcess(unittest.TestCase):
     Exercises launch_tor_with_config when we write a torrc to disk.
     """
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       control_port = random_port()
       control_socket, tor_process = None, None
 
@@ -479,7 +468,7 @@ class TestProcess(unittest.TestCase):
     if test.tor_version() < stem.version.Requirement.TORRC_VIA_STDIN:
       skip('(requires %s)' % stem.version.Requirement.TORRC_VIA_STDIN)
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       control_port = random_port()
       control_socket, tor_process = None, None
 
@@ -521,7 +510,7 @@ class TestProcess(unittest.TestCase):
     #   [warn] Failed to parse/validate config: Failed to bind one of the listener ports.
     #   [err] Reading config failed--see warnings above.
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       both_ports = random_port()
 
       try:
@@ -543,7 +532,7 @@ class TestProcess(unittest.TestCase):
     Runs launch_tor where it times out before completing.
     """
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       start_time = time.time()
 
       try:
@@ -575,7 +564,7 @@ class TestProcess(unittest.TestCase):
     elif test.tor_version() < stem.version.Requirement.TAKEOWNERSHIP:
       skip('(requires %s)' % stem.version.Requirement.TAKEOWNERSHIP)
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       sleep_process = subprocess.Popen(['sleep', '60'])
 
       tor_process = stem.process.launch_tor_with_config(
@@ -619,7 +608,7 @@ class TestProcess(unittest.TestCase):
     if test.tor_version() < stem.version.Requirement.TAKEOWNERSHIP:
       skip('(requires %s)' % stem.version.Requirement.TAKEOWNERSHIP)
 
-    with tmp_directory() as data_directory:
+    with tempfile.TemporaryDirectory() as data_directory:
       control_port = random_port()
 
       tor_process = stem.process.launch_tor_with_config(
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index 4d83d695..2a7ac07a 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -361,13 +361,14 @@ class TestSystem(unittest.TestCase):
     Checks the stem.util.system.pid_by_open_file function.
     """
 
+    # check a directory that doesn't exist
+
+    self.assertEqual(None, stem.util.system.pid_by_open_file('/no/such/path'))
+
     # check a directory that exists, but isn't claimed by any application
-    tmpdir = tempfile.mkdtemp()
-    self.assertEqual(None, stem.util.system.pid_by_open_file(tmpdir))
 
-    # check a directory that doesn't exist
-    os.rmdir(tmpdir)
-    self.assertEqual(None, stem.util.system.pid_by_open_file(tmpdir))
+    with tempfile.TemporaryDirectory() as tmpdir:
+      self.assertEqual(None, stem.util.system.pid_by_open_file(tmpdir))
 
   @require_path
   def test_pids_by_user(self):
diff --git a/test/unit/manual.py b/test/unit/manual.py
index eb76d6e9..e1891ca4 100644
--- a/test/unit/manual.py
+++ b/test/unit/manual.py
@@ -76,6 +76,10 @@ EXPECTED_CONFIG_OPTIONS['Bridge'] = stem.manual.ConfigOption(
 
 CACHED_MANUAL = None
 
+TEMP_DIR_MOCK = Mock()
+TEMP_DIR_MOCK.__enter__ = Mock(return_value = '/no/such/path')
+TEMP_DIR_MOCK.__exit__ = Mock(return_value = False)
+
 
 def _cached_manual():
   global CACHED_MANUAL
@@ -230,16 +234,14 @@ class TestManual(unittest.TestCase):
     exc_msg = 'We require a2x from asciidoc to provide a man page'
     self.assertRaisesWith(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file')
 
-  @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
-  @patch('shutil.rmtree', Mock())
+  @patch('tempfile.TemporaryDirectory', Mock(return_value = TEMP_DIR_MOCK))
   @patch('stem.manual.open', Mock(side_effect = IOError('unable to write to file')), create = True)
   @patch('stem.util.system.is_available', Mock(return_value = True))
   def test_download_man_page_when_unable_to_write(self):
     exc_msg = "Unable to download tor's manual from https://gitweb.torproject.org/tor.git/plain/doc/tor.1.txt to /no/such/path/tor.1.txt: unable to write to file"
     self.assertRaisesWith(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file')
 
-  @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
-  @patch('shutil.rmtree', Mock())
+  @patch('tempfile.TemporaryDirectory', Mock(return_value = TEMP_DIR_MOCK))
   @patch('stem.manual.open', Mock(return_value = io.BytesIO()), create = True)
   @patch('stem.util.system.is_available', Mock(return_value = True))
   @patch('urllib.request.urlopen', Mock(side_effect = urllib.request.URLError('<urlopen error [Errno -2] Name or service not known>')))
@@ -247,8 +249,7 @@ class TestManual(unittest.TestCase):
     exc_msg = "Unable to download tor's manual from https://www.atagar.com/foo/bar to /no/such/path/tor.1.txt: <urlopen error <urlopen error [Errno -2] Name or service not known>>"
     self.assertRaisesWith(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
 
-  @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
-  @patch('shutil.rmtree', Mock())
+  @patch('tempfile.TemporaryDirectory', Mock(return_value = TEMP_DIR_MOCK))
   @patch('stem.manual.open', Mock(return_value = io.BytesIO()), create = True)
   @patch('stem.util.system.call', Mock(side_effect = stem.util.system.CallError('call failed', 'a2x -f manpage /no/such/path/tor.1.txt', 1, None, None, 'call failed')))
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -257,8 +258,7 @@ class TestManual(unittest.TestCase):
     exc_msg = "Unable to run 'a2x -f manpage /no/such/path/tor.1.txt': call failed"
     self.assertRaisesWith(IOError, exc_msg, stem.manual.download_man_page, '/tmp/no_such_file', url = 'https://www.atagar.com/foo/bar')
 
-  @patch('tempfile.mkdtemp', Mock(return_value = '/no/such/path'))
-  @patch('shutil.rmtree', Mock())
+  @patch('tempfile.TemporaryDirectory', Mock(return_value = TEMP_DIR_MOCK))
   @patch('stem.manual.open', create = True)
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))





More information about the tor-commits mailing list