@@ -448,6 +448,15 @@ def unstub(*objs):
448448 If you don't pass in any argument, *all* registered mocks and
449449 patched modules, classes etc. will be unstubbed.
450450
451+ You can also unstub a single method/function target, e.g.::
452+
453+ unstub(os.path.exists)
454+ unstub("os.path.exists")
455+ unstub(cat.meow)
456+
457+ In these cases only that one attribute is restored, while other stubs on
458+ the same object stay active.
459+
451460 Note that additionally, the underlying registry will be cleaned.
452461 After an `unstub` you can't :func:`verify` anymore because all
453462 interactions will be forgotten.
@@ -457,13 +466,55 @@ def unstub(*objs):
457466 for obj in objs :
458467 if isinstance (obj , str ):
459468 obj = get_obj (obj )
460- mock_registry .unstub (obj )
461- patcher .unstub_matching (obj )
469+
470+ # mock_registry.unstub(obj)
471+ # patcher.unstub_matching(obj)
472+ if (
473+ mock_registry .unstub (obj )
474+ or patcher .unstub_matching (obj )
475+ ):
476+ return
477+
478+ resolved_target = _resolve_unstub_attr_target (obj )
479+ if resolved_target is None :
480+ continue
481+
482+ host , attr_name = resolved_target
483+ host_mock = mock_registry .mock_for (host )
484+ if host_mock is not None :
485+ host_mock .unstub_method (attr_name )
462486 else :
463487 mock_registry .unstub_all ()
464488 patcher .unstub_all ()
465489
466490
491+ def _resolve_unstub_attr_target (target ):
492+ if not callable (target ):
493+ return None
494+
495+ host = getattr (target , "__self__" , None )
496+ attr_name = getattr (target , "__name__" , None )
497+ if host is not None and attr_name is not None :
498+ return host , attr_name
499+
500+ target_function = _unwrap_unstub_target (target )
501+ for theMock in mock_registry .get_registered_mocks ():
502+ for method_name , patch in theMock ._methods_to_unstub .items ():
503+ replacement = getattr (patch , "replacement" , None )
504+ if _unwrap_unstub_target (replacement ) is target_function :
505+ return theMock .mocked_obj , method_name
506+
507+ return None
508+
509+
510+
511+ def _unwrap_unstub_target (target ):
512+ if isinstance (target , (staticmethod , classmethod )):
513+ return target .__func__
514+
515+ return getattr (target , "__func__" , target )
516+
517+
467518def forget_invocations (* objs ):
468519 """Forget all invocations of given objs.
469520
0 commit comments