diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 1a6c1a606c5a257..24a5b74807f43c9 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -671,12 +671,14 @@ def __dir__(self): extras = self._mock_methods or [] from_type = dir(type(self)) from_dict = list(self.__dict__) + from_child_mocks = [ + m_name for m_name, m_value in self._mock_children.items() + if m_value is not _deleted] from_type = [e for e in from_type if not e.startswith('_')] from_dict = [e for e in from_dict if not e.startswith('_') or _is_magic(e)] - return sorted(set(extras + from_type + from_dict + - list(self._mock_children))) + return sorted(set(extras + from_type + from_dict + from_child_mocks)) def __setattr__(self, name, value): diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index c7bfa277b511ca9..7102a604785f50a 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -748,6 +748,15 @@ def test_filter_dir(self): patcher.stop() + def test_dir_does_not_include_deleted_attributes(self): + mock = Mock() + mock.child.return_value = 1 + + self.assertIn('child', dir(mock)) + del mock.child + self.assertNotIn('child', dir(mock)) + + def test_configure_mock(self): mock = Mock(foo='bar') self.assertEqual(mock.foo, 'bar') diff --git a/Misc/NEWS.d/next/Library/2018-10-27-11-54-12.bpo-35082.HDj1nr.rst b/Misc/NEWS.d/next/Library/2018-10-27-11-54-12.bpo-35082.HDj1nr.rst new file mode 100644 index 000000000000000..45a0729506e3926 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-10-27-11-54-12.bpo-35082.HDj1nr.rst @@ -0,0 +1,2 @@ +Don't return deleted attributes when calling dir on a +:class:`unittest.mock.Mock`.