From fa069ce5e8f8e6dfc690a4988b0ddade76bf9e4e 2017-01-20 22:11:46 From: Christopher Welborn Date: 2017-01-20 22:11:46 Subject: [PATCH] IOStream: Ignore missing attrs from `dir()`. #6386 In some cases calling `getattr()` on attributes found with `dir()` will fail. This is seen in Issue #6386. The attribute in that issue is `name`, which is not needed for `IOStream` to work correctly. This fix will set the attribute to `None` and move on. Tests were added in `IPython.utils.tests.test_io.py` to catch any regressions. --- diff --git a/IPython/utils/io.py b/IPython/utils/io.py index 173d602..72c880c 100644 --- a/IPython/utils/io.py +++ b/IPython/utils/io.py @@ -37,7 +37,12 @@ class IOStream: def clone(meth): return not hasattr(self, meth) and not meth.startswith('_') for meth in filter(clone, dir(stream)): - setattr(self, meth, getattr(stream, meth)) + try: + val = getattr(stream, meth) + except AttributeError: + pass + else: + setattr(self, meth, val) def __repr__(self): cls = self.__class__ diff --git a/IPython/utils/tests/test_io.py b/IPython/utils/tests/test_io.py index b23a4a3..0b528c7 100644 --- a/IPython/utils/tests/test_io.py +++ b/IPython/utils/tests/test_io.py @@ -17,7 +17,7 @@ import unittest import nose.tools as nt from IPython.testing.decorators import skipif, skip_win32 -from IPython.utils.io import Tee, capture_output +from IPython.utils.io import IOStream, Tee, capture_output from IPython.utils.py3compat import doctest_refactor_print from IPython.utils.tempdir import TemporaryDirectory @@ -68,6 +68,19 @@ def test_io_init(): # just test for string equality. assert 'IPython.utils.io.IOStream' in classname, classname +def test_IOStream_init(): + """IOStream initializes from a file-like object missing attributes. """ + # Cause a failure from getattr and dir(). (Issue #6386) + class BadStringIO(StringIO): + def __dir__(self): + attrs = super(StringIO, self).__dir__() + attrs.append('name') + return attrs + + iostream = IOStream(BadStringIO()) + iostream.write('hi, bad iostream\n') + assert not hasattr(iostream, 'name') + def test_capture_output(): """capture_output() context works"""