Author: Amaury Forgeot d'Arc <[email protected]>
Branch: py3.6
Changeset: r93354:2d7ef69f48bd
Date: 2017-12-10 21:33 +0100
http://bitbucket.org/pypy/pypy/changeset/2d7ef69f48bd/

Log:    Scandir: Add a context manager, and a ResourceWarning when it is not
        explictly closed.

diff --git a/pypy/module/posix/interp_scandir.py 
b/pypy/module/posix/interp_scandir.py
--- a/pypy/module/posix/interp_scandir.py
+++ b/pypy/module/posix/interp_scandir.py
@@ -51,11 +51,25 @@
         self.dirfd = dirfd
         self.w_path_prefix = w_path_prefix
         self.result_is_bytes = result_is_bytes
+        self.register_finalizer(space)
 
-    @rgc.must_be_light_finalizer
-    def __del__(self):
-        if self.dirp:
-            rposix_scandir.closedir(self.dirp)
+    def _finalize_(self):
+        if not self.dirp:
+            return
+        space = self.space
+        try:
+            msg = ("unclosed scandir iterator %s" %
+                   space.text_w(space.repr(self)))
+            space.warn(space.newtext(msg), space.w_ResourceWarning)
+        except OperationError as e:
+            # Spurious errors can appear at shutdown
+            if e.match(space, space.w_Warning):
+                e.write_unraisable(space, '', self)
+        self._close()
+
+    def _close(self):
+        rposix_scandir.closedir(self.dirp)
+        self.dirp = rposix_scandir.NULL_DIRP
 
     def iter_w(self):
         return self
@@ -96,16 +110,31 @@
             #
             known_type = rposix_scandir.get_known_type(entry)
             inode = rposix_scandir.get_inode(entry)
+        except:
+            self._close()
+            raise
         finally:
             self._in_next = False
         direntry = W_DirEntry(self, name, known_type, inode)
         return direntry
 
+    def close_w(self):
+        self._close()
+
+    def enter_w(self):
+        return self
+
+    def exit_w(self, space, __args__):
+        self._close()
+
 
 W_ScandirIterator.typedef = TypeDef(
     'posix.ScandirIterator',
     __iter__ = interp2app(W_ScandirIterator.iter_w),
     __next__ = interp2app(W_ScandirIterator.next_w),
+    __enter__ = interp2app(W_ScandirIterator.enter_w),
+    __exit__ = interp2app(W_ScandirIterator.exit_w),
+    close = interp2app(W_ScandirIterator.close_w),
 )
 W_ScandirIterator.typedef.acceptable_as_base_class = False
 
diff --git a/pypy/module/posix/test/test_scandir.py 
b/pypy/module/posix/test/test_scandir.py
--- a/pypy/module/posix/test/test_scandir.py
+++ b/pypy/module/posix/test/test_scandir.py
@@ -170,3 +170,34 @@
         posix = self.posix
         d = next(posix.scandir(self.dir1))
         assert repr(d) == "<DirEntry 'file1'>"
+
+    def test_resource_warning(self):
+        posix = self.posix
+        import warnings, gc
+        iterator = posix.scandir(self.dir1)
+        next(iterator)
+        with warnings.catch_warnings(record=True) as l:
+            warnings.simplefilter("always")
+            del iterator
+            gc.collect()
+        assert isinstance(l[0].message, ResourceWarning)
+        #
+        iterator = posix.scandir(self.dir1)
+        next(iterator)
+        with warnings.catch_warnings(record=True) as l:
+            warnings.simplefilter("always")
+            iterator.close()
+            del iterator
+            gc.collect()
+        assert len(l) == 0
+
+    def test_context_manager(self):
+        posix = self.posix
+        import warnings, gc
+        with warnings.catch_warnings(record=True) as l:
+            warnings.simplefilter("always")
+            with posix.scandir(self.dir1) as iterator:
+                next(iterator)
+            del iterator
+            gc.collect()
+        assert not l
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to