diff options
author | Claudiu Popa <cpopa@cloudbasesolutions.com> | 2014-12-15 17:18:46 +0200 |
---|---|---|
committer | Claudiu Popa <cpopa@cloudbasesolutions.com> | 2014-12-15 17:18:46 +0200 |
commit | 72d17ce446c7e76c9d385e65c91a80b9a412250e (patch) | |
tree | 8747c0676637dcc05bddccb3aa5b50e8205abd9d | |
parent | 915c7107f9bfa4d981107b5cebf20dad19dece75 (diff) | |
download | astroid-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-- | ChangeLog | 14 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 29 | ||||
-rw-r--r-- | astroid/tests/unittest_scoped_nodes.py | 32 |
3 files changed, 57 insertions, 18 deletions
@@ -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): |