Navigation

Source code for _pytest.monkeypatch

""" monkeypatching and mocking functionality. """importos,sysfrompy.builtinimport_basestringdefpytest_funcarg__monkeypatch(request):"""The returned ``monkeypatch`` funcarg provides these helper methods to modify objects, dictionaries or os.environ:: monkeypatch.setattr(obj, name, value, raising=True) monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) monkeypatch.setenv(name, value, prepend=False) monkeypatch.delenv(name, value, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path) All modifications will be undone after the requesting test function has finished. The ``raising`` parameter determines if a KeyError or AttributeError will be raised if the set/deletion operation has no target. """mpatch=monkeypatch()request.addfinalizer(mpatch.undo)returnmpatchdefderive_importpath(import_path):importpytestifnotisinstance(import_path,_basestring)or"."notinimport_path:raiseTypeError("must be absolute import path string, not %r"%(import_path,))rest=[]target=import_pathwhiletarget:try:obj=__import__(target,None,None,"__doc__")exceptImportError:if"."notintarget:__tracebackhide__=Truepytest.fail("could not import any sub part: %s"%import_path)target,name=target.rsplit(".",1)rest.append(name)else:assertresttry:whilelen(rest)>1:attr=rest.pop()obj=getattr(obj,attr)attr=rest[0]getattr(obj,attr)exceptAttributeError:__tracebackhide__=Truepytest.fail("object %r has no attribute %r"%(obj,attr))returnattr,objclassNotset:def__repr__(self):return"<notset>"notset=Notset()

[docs]classmonkeypatch:""" Object keeping a record of setattr/item/env/syspath changes. """def__init__(self):self._setattr=[]self._setitem=[]self._cwd=None

[docs]defsetattr(self,target,name,value=notset,raising=True):""" Set attribute value on target, memorizing the old value. By default raise AttributeError if the attribute did not exist. For convenience you can specify a string as ``target`` which will be interpreted as a dotted import path, with the last part being the attribute name. Example: ``monkeypatch.setattr("os.getcwd", lambda x: "/")`` would set the ``getcwd`` function of the ``os`` module. The ``raising`` value determines if the setattr should fail if the attribute is not already present (defaults to True which means it will raise). """__tracebackhide__=Trueimportinspectifvalueisnotset:ifnotisinstance(target,_basestring):raiseTypeError("use setattr(target, name, value) or ""setattr(target, value) with target being a dotted ""import string")value=namename,target=derive_importpath(target)oldval=getattr(target,name,notset)ifraisingandoldvalisnotset:raiseAttributeError("%r has no attribute %r"%(target,name))# avoid class descriptors like staticmethod/classmethodifinspect.isclass(target):oldval=target.__dict__.get(name,notset)self._setattr.insert(0,(target,name,oldval))setattr(target,name,value)

[docs]defdelattr(self,target,name=notset,raising=True):""" Delete attribute ``name`` from ``target``, by default raise AttributeError it the attribute did not previously exist. If no ``name`` is specified and ``target`` is a string it will be interpreted as a dotted import path with the last part being the attribute name. If ``raising`` is set to False, no exception will be raised if the attribute is missing. """__tracebackhide__=Trueifnameisnotset:ifnotisinstance(target,_basestring):raiseTypeError("use delattr(target, name) or ""delattr(target) with target being a dotted ""import string")name,target=derive_importpath(target)ifnothasattr(target,name):ifraising:raiseAttributeError(name)else:self._setattr.insert(0,(target,name,getattr(target,name,notset)))delattr(target,name)

[docs]defdelitem(self,dic,name,raising=True):""" Delete ``name`` from dict. Raise KeyError if it doesn't exist. If ``raising`` is set to False, no exception will be raised if the key is missing. """ifnamenotindic:ifraising:raiseKeyError(name)else:self._setitem.insert(0,(dic,name,dic.get(name,notset)))deldic[name]

[docs]defsetenv(self,name,value,prepend=None):""" Set environment variable ``name`` to ``value``. If ``prepend`` is a character, read the current environment variable value and prepend the ``value`` adjoined with the ``prepend`` character."""value=str(value)ifprependandnameinos.environ:value=value+prepend+os.environ[name]self.setitem(os.environ,name,value)

[docs]defdelenv(self,name,raising=True):""" Delete ``name`` from the environment. Raise KeyError it does not exist. If ``raising`` is set to False, no exception will be raised if the environment variable is missing. """self.delitem(os.environ,name,raising=raising)

[docs]defchdir(self,path):""" Change the current working directory to the specified path path can be a string or a py.path.local object """ifself._cwdisNone:self._cwd=os.getcwd()ifhasattr(path,"chdir"):path.chdir()else:os.chdir(path)

[docs]defundo(self):""" Undo previous changes. This call consumes the undo stack. Calling it a second time has no effect unless you do more monkeypatching after the undo call."""forobj,name,valueinself._setattr:ifvalueisnotnotset:setattr(obj,name,value)else:delattr(obj,name)self._setattr[:]=[]fordictionary,name,valueinself._setitem:ifvalueisnotset:try:deldictionary[name]exceptKeyError:pass# was already deleted, so we have the desired stateelse:dictionary[name]=valueself._setitem[:]=[]ifhasattr(self,'_savesyspath'):sys.path[:]=self._savesyspathdelself._savesyspathifself._cwdisnotNone:os.chdir(self._cwd)self._cwd=None