summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <cpopa@cloudbasesolutions.com>2014-12-15 17:18:46 +0200
committerClaudiu Popa <cpopa@cloudbasesolutions.com>2014-12-15 17:18:46 +0200
commit72d17ce446c7e76c9d385e65c91a80b9a412250e (patch)
tree8747c0676637dcc05bddccb3aa5b50e8205abd9d
parent915c7107f9bfa4d981107b5cebf20dad19dece75 (diff)
downloadastroid-72d17ce446c7e76c9d385e65c91a80b9a412250e.tar.gz
Rework the file_stream API.
Restore file_stream to a propery, but deprecate it in favour of the newly added method Module.stream. By using a method instead of a property, it will be easier to properly close the file right after it is used, which will ensure that no file descriptors are leaked. Until now, due to the fact that a module was cached, it was not possible to close the file_stream anywhere. file_stream will start emitting PendingDeprecationWarnings in astroid 1.4, DeprecationWarnings in astroid 1.5 and it will be finally removed in astroid 1.6.
-rw-r--r--ChangeLog14
-rw-r--r--astroid/scoped_nodes.py29
-rw-r--r--astroid/tests/unittest_scoped_nodes.py32
3 files changed, 57 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index c16e462..f88f4b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,11 +3,15 @@ Change log for the astroid package (used to be astng)
--
- * Restore the file_stream API. Module.file_stream is again
- a property, but each opened file is cached. Module node
- gains a new API, 'close', which should be called for closing
- all the gathered file streams. This is consistent with how
- the API worked pre-astroid 1.3 (meaning that no seek is required).
+ * Restore file_stream to a property, but deprecate it in favour of
+ the newly added method Module.stream. By using a method instead of a
+ property, it will be easier to properly close the file right
+ after it is used, which will ensure that no file descriptors are
+ leaked. Until now, due to the fact that a module was cached,
+ it was not possible to close the file_stream anywhere.
+ file_stream will start emitting PendingDeprecationWarnings in
+ astroid 1.4, DeprecationWarnings in astroid 1.5 and it will
+ be finally removed in astroid 1.6.
* Add inference tips for 'tuple', 'list', 'dict' and 'set' builtins.
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 89137bb..f423bad 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -24,6 +24,7 @@ from __future__ import with_statement
__doctype__ = "restructuredtext en"
import sys
+import warnings
from itertools import chain
try:
from io import BytesIO
@@ -256,23 +257,37 @@ class Module(LocalsDictNodeNG):
self.locals = self.globals = {}
self.body = []
self.future_imports = set()
- self._streams = []
- @property
- def file_stream(self):
+ def _get_stream(self):
if self.file_bytes is not None:
return BytesIO(self.file_bytes)
if self.file is not None:
stream = open(self.file, 'rb')
- self._streams.append(stream)
return stream
return None
+ @property
+ def file_stream(self):
+ warnings.warn("file_stream property is deprecated and "
+ "it is slated for removal in Pylint 1.6."
+ "Use the new method 'stream' instead.",
+ PendingDeprecationWarning,
+ stacklevel=2)
+ return self._get_stream()
+
+ def stream(self):
+ """Get a stream to the underlying file or bytes."""
+ return self._get_stream()
+
def close(self):
"""Close the underlying file streams."""
- for stream in self._streams:
- stream.close()
- self._streams[:] = []
+ warnings.warn("close method is deprecated and it is "
+ "slated for removal in Pylint 1.6, along "
+ "with 'file_stream' property. "
+ "Its behaviour is replaced by managing each "
+ "file stream returned by the 'stream' method.",
+ PendingDeprecationWarning,
+ stacklevel=2)
def block_range(self, lineno):
"""return block line numbers.
diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py
index 5a94ae1..496b67f 100644
--- a/astroid/tests/unittest_scoped_nodes.py
+++ b/astroid/tests/unittest_scoped_nodes.py
@@ -22,10 +22,12 @@ import os
import sys
from functools import partial
import unittest
+import warnings
from astroid import YES, builder, nodes, scoped_nodes, \
InferenceError, NotFoundError, NoDefault
from astroid.bases import BUILTINS, Instance, BoundMethod, UnboundMethod
+from astroid import __pkginfo__
from astroid import test_utils
from astroid.tests import resources
@@ -191,12 +193,30 @@ class ModuleNodeTest(ModuleLoader, unittest.TestCase):
def test_file_stream_api(self):
path = resources.find('data/all.py')
astroid = builder.AstroidBuilder().file_build(path, 'all')
- self.assertIsNot(astroid.file_stream, astroid.file_stream)
- self.assertEqual(len(astroid._streams), 2)
- for stream in astroid._streams:
- self.assertFalse(stream.closed)
- astroid.close()
- self.assertEqual(astroid._streams, [])
+ if __pkginfo__.numversion >= (1, 6):
+ # file_stream is slated for removal in astroid 1.6.
+ with self.assertRaises(AttributeError):
+ astroid.file_stream
+ else:
+ # Until astroid 1.6, Module.file_stream will emit
+ # PendingDeprecationWarning in 1.4, DeprecationWarning
+ # in 1.5 and finally it will be removed in 1.6, leaving
+ # only Module.stream as the recommended way to retrieve
+ # its file stream.
+ with warnings.catch_warnings(record=True) as cm:
+ warnings.simplefilter("always")
+ self.assertIsNot(astroid.file_stream, astroid.file_stream)
+ self.assertGreater(len(cm), 1)
+ self.assertEqual(cm[0].category, PendingDeprecationWarning)
+
+ def test_stream_api(self):
+ path = resources.find('data/all.py')
+ astroid = builder.AstroidBuilder().file_build(path, 'all')
+ stream = astroid.stream()
+ self.assertTrue(hasattr(stream, 'close'))
+ with stream:
+ with open(path, 'rb') as file_io:
+ self.assertEqual(stream.read(), file_io.read())
class FunctionNodeTest(ModuleLoader, unittest.TestCase):