Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions Doc/library/unittest.mock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,16 +231,20 @@ the *new_callable* argument to :func:`patch`.
Create a new :class:`Mock` object. :class:`Mock` takes several optional arguments
that specify the behaviour of the Mock object:

* *spec*: This can be either a list of strings or an existing object (a
class or instance) that acts as the specification for the mock object. If
you pass in an object then a list of strings is formed by calling dir on
* *spec*: This can be either a list or tuple of strings,
or an existing object (a class or instance)
that acts as the specification for the mock object.
If you pass in an object then a list of strings is formed by calling dir on
the object (excluding unsupported magic attributes and methods).
Accessing any attribute not in this list will raise an :exc:`AttributeError`.

If *spec* is an object (rather than a list of strings) then
:attr:`~object.__class__` returns the class of the spec object. This
allows mocks to pass :func:`isinstance` tests.

.. versionchanged:: next
:func:`dir` now works for a mock created with a tuple *spec*.

* *spec_set*: A stricter variant of *spec*. If used, attempting to *set*
or get an attribute on the mock that isn't on the object passed as
*spec_set* will raise an :exc:`AttributeError`.
Expand Down Expand Up @@ -448,9 +452,9 @@ the *new_callable* argument to :func:`patch`.

.. method:: mock_add_spec(spec, spec_set=False)

Add a spec to a mock. *spec* can either be an object or a
list of strings. Only attributes on the *spec* can be fetched as
attributes from the mock.
Add a spec to a mock.
*spec* can either be an object or a list or tuple of strings.
Only attributes on the *spec* can be fetched as attributes from the mock.

If *spec_set* is true then only attributes on the spec can be set.

Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_unittest/testmock/testmock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,10 @@ def test_dir(self):
mock.__iter__ = lambda s: iter([])
self.assertIn('__iter__', dir(mock))

# spec from a tuple
mock_tuple_spec = Mock(spec=('something',))
self.assertIn('something', dir(mock_tuple_spec))


def test_dir_from_spec(self):
mock = Mock(spec=unittest.TestCase)
Expand Down
2 changes: 1 addition & 1 deletion Lib/unittest/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ def __dir__(self):
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 + from_child_mocks))
return sorted({*extras, *from_type, *from_dict, *from_child_mocks})


def __setattr__(self, name, value):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`dir` on :class:`unittest.mock.Mock` objects
created with a tuple *spec*: it raised :exc:`TypeError`.
Such *spec* is now also documented.
Loading