Repository: wireservice/csvkit
Issue: Lingering "unclosed file" warnings

Was originally tracked as wireservice/agate#647, but really this was a csvkit issue. I've fixed most of them. Here are the remainder:

```
test_fixed_no_inference (tests.test_convert.test_fixed.TestFixed) ... /Users/cgroskopf/src/csvkit/csvkit/cli.py:124: ResourceWarning: unclosed file <_io.TextIOWrapper name='examples/testfixed_schema_no_inference.csv' mode='rt' encoding='utf-8'>
ok

test_invalid_options (tests.test_utilities.test_csvcut.TestCSVCut) ... /Users/cgroskopf/src/csvkit/csvkit/cli.py:294: ResourceWarning: unclosed file <_io.TextIOWrapper name='examples/dummy.csv' mode='rt' encoding='utf-8'>
ok

test_invalid_options (tests.test_utilities.test_csvgrep.TestCSVGrep) ... /Users/cgroskopf/src/csvkit/csvkit/cli.py:294: ResourceWarning: unclosed file <_io.TextIOWrapper name='examples/dummy.csv' mode='rt' encoding='utf-8'>
ok

test_duplicate_keys (tests.test_utilities.test_csvjson.TestCSVJSON) ... /Users/cgroskopf/src/csvkit/tests/test_utilities/test_csvjson.py:49: DeprecationWarning: Please use assertRaisesRegex instead.
ok

test_invalid_options (tests.test_utilities.test_csvstat.TestCSVStat) ... /Users/cgroskopf/src/csvkit/csvkit/cli.py:294: ResourceWarning: unclosed file <_io.TextIOWrapper name='examples/dummy.csv' mode='rt' encoding='utf-8'>
ok
```


Changed files:
tests/test_utilities/test_sql2csv.py, tests/utils.py, csvkit/cli.py, tests/test_utilities/test_csvclean.py, csvkit/utilities/csvjson.py, tests/test_utilities/test_csvjson.py

diff --git a/csvkit/cli.py b/csvkit/cli.py
index 6c37e2e..5038f2e 100644
--- a/csvkit/cli.py
+++ b/csvkit/cli.py
@@ -14,13 +14,6 @@ import six
 from csvkit.exceptions import ColumnIdentifierError, RequiredHeaderError
 
 
-def lazy_opener(fn):
-    def wrapped(self, *args, **kwargs):
-        self._lazy_open()
-        fn(*args, **kwargs)
-    return wrapped
-
-
 class LazyFile(six.Iterator):
     """
     A proxy for a File object that delays opening it until
@@ -121,13 +114,14 @@ class CSVKitUtility(object):
         else:
             self.output_file = self.output_file_handle
 
-        self.main()
-
-        if 'f' not in self.override_flags:
-            self.input_file.close()
+        try:
+            self.main()
+        finally:
+            if 'f' not in self.override_flags:
+                self.input_file.close()
 
-        if not self.output_file_handle:
-            self.output_file.close()
+            if self.output_file_handle is None:
+                self.output_file.close()
 
     def main(self):
         """
diff --git a/csvkit/utilities/csvjson.py b/csvkit/utilities/csvjson.py
index 09d8a20..1d7cf6f 100644
--- a/csvkit/utilities/csvjson.py
+++ b/csvkit/utilities/csvjson.py
@@ -170,7 +170,6 @@ class CSVJSON(CSVKitUtility):
             table.to_json(self.output_file, key=self.args.key, newline=self.args.streamOutput, **json_kwargs)
 
 
-
 def launch_new_instance():
     utility = CSVJSON()
     utility.run()
diff --git a/tests/test_utilities/test_csvclean.py b/tests/test_utilities/test_csvclean.py
index 9b74ab5..3fd2616 100644
--- a/tests/test_utilities/test_csvclean.py
+++ b/tests/test_utilities/test_csvclean.py
@@ -47,8 +47,6 @@ class TestCSVClean(CSVKitTestCase, EmptyFileTests):
             os.remove('examples/bad_err.csv')
             os.remove('examples/bad_out.csv')
 
-        output_file.close()
-
     def test_dry_run(self):
         output = self.get_output_as_io(['-n', 'examples/bad.csv'])
         self.assertFalse(os.path.exists('examples/bad_err.csv'))
diff --git a/tests/test_utilities/test_csvjson.py b/tests/test_utilities/test_csvjson.py
index 1f8ef77..aad7eb6 100644
--- a/tests/test_utilities/test_csvjson.py
+++ b/tests/test_utilities/test_csvjson.py
@@ -38,15 +38,15 @@ class TestCSVJSON(CSVKitTestCase, EmptyFileTests):
         output = self.get_output(['-i', '4', 'examples/dummy.csv'])
         js = json.loads(output)
         self.assertDictEqual(js[0], {'a': True, 'c': 3.0, 'b': 2.0})
-        self.assertRegexpMatches(output, '        "a": true,')
+        six.assertRegex(self, output, '        "a": true,')
 
     def test_keying(self):
         js = json.loads(self.get_output(['-k', 'a', 'examples/dummy.csv']))
         self.assertDictEqual(js, {'true': {'a': True, 'c': 3.0, 'b': 2.0}})
 
-    def test_duplicate_keys(self):        
-        utility = CSVJSON(['-k', 'a', 'examples/dummy3.csv'])
-        self.assertRaisesRegexp(ValueError, 'Value True is not unique in the key column\\.', utility.run)
+    def test_duplicate_keys(self):
+        utility = CSVJSON(['-k', 'a', 'examples/dummy3.csv'], six.StringIO())
+        six.assertRaisesRegex(self, ValueError, 'Value True is not unique in the key column\\.', utility.run)
 
     def test_geojson(self):
         geojson = json.loads(self.get_output(['--lat', 'latitude', '--lon', 'longitude', 'examples/test_geo.csv']))
diff --git a/tests/test_utilities/test_sql2csv.py b/tests/test_utilities/test_sql2csv.py
index f841aa0..d620a3c 100644
--- a/tests/test_utilities/test_sql2csv.py
+++ b/tests/test_utilities/test_sql2csv.py
@@ -105,9 +105,9 @@ class TestSQL2CSV(CSVKitTestCase, EmptyFileTests):
 
     # def test_wildcard_on_postgresql(self):
     #     db = postgresql_scheme + ':///dummy_test'
-    #
+
     #     self.csvsql('examples/iris.csv', db)
     #     csv = self.get_output(['--db', db, '--query', "select * from foo where species LIKE '%'"])
-    #
+
     #     self.assertTrue('sepal_length,sepal_width,petal_length,petal_width,species' in csv)
     #     self.assertTrue('5.1,3.5,1.4,0.2,Iris-setosa' in csv)
diff --git a/tests/utils.py b/tests/utils.py
index c772263..e4ce81d 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -35,11 +35,7 @@ class CSVKitTestCase(unittest.TestCase):
         output_file = six.StringIO()
         utility = self.Utility(args, output_file)
         utility.run()
-
-        output = output_file.getvalue()
-        output_file.close()
-
-        return output
+        return output_file.getvalue()
 
     def get_output_as_io(self, args):
         return six.StringIO(self.get_output(args))
@@ -90,8 +86,6 @@ class NamesTests(object):
         with self.assertRaises(RequiredHeaderError):
             utility.run()
 
-        output_file.close()
-
 
 class ColumnsTests(object):
     def test_invalid_column(self):