Source code for mockproc.mockprocess
import os,tempfile,shutil,logging
log = logging.getLogger( __name__ )
[docs]class MockProc( object ):
"""Provides basic path-overriding process mocks for nose (or similar) test frameworks
You would normally use a MockProc where you have code which uses subprocess
to call an external program which would have negative effects were it to
actually be called within your test framework.
Limitations:
Only works on PATH-looked-up executables, so e.g. /etc/init.d/* executables
will *not* be overridden.
Will not give you any notice if the mocking/faking fails.
Default script template doesn't look at arguments or do "expect"-style
parsing of input, so it just always produces the specified results.
Not thread-safe.
"""
[docs] def __init__( self, bindir=None ):
self.scripts = {}
self.bindir = bindir
[docs] def __enter__( self ):
"""Write scripts to bindir (possibly creating it) and set PATH to use it"""
if not self.bindir:
self.bindir = tempfile.mkdtemp()
bindir = self.bindir
try:
if not os.path.isdir( bindir ):
os.makedirs( bindir )
except (IOError,OSError,TypeError), err:
log.warn( 'Could not create bin directory for mocks: %s', bindir )
raise RuntimeError( """Unable to create test binary directory: %s"""%( bindir, ))
os.environ['PATH'] = self.bindir + os.pathsep + os.environ.get( 'PATH', '' )
for scripts in self.scripts.values():
if scripts:
self.write_script( scripts[-1] )
enter = __enter__
[docs] def __exit__( self ):
"""Delete bindir and remove from PATH"""
if self.bindir:
os.environ['PATH'] = os.environ.get( 'PATH', '' ).replace( os.pathsep + self.bindir, '' )
for scripts in self.scripts.values():
if scripts:
self.delete_script( scripts[-1] )
shutil.rmtree( self.bindir, ignore_errors=True )
exit = __exit__
[docs] def write_script( self, description ):
if not description.get('script'):
description['script'] = self.script_template % description
description['filename'] = filename = os.path.join( self.bindir, description['executable'] )
fh = open( filename, 'w' )
fh.write( description['script'] )
fh.close()
os.chmod( filename,0755 )
return filename
[docs] def delete_script( self, description ):
try:
filename = os.path.join( self.bindir, description['executable'] )
os.remove( filename )
except (OSError,IOError,TypeError), err:
return False
else:
return True
[docs] def append(
self,
executable,
returncode = 0,
stdout = None,
stderr = None,
script = None,
):
"""Add a new script to the bin-dir, 'pushes' the script into the stack..."""
if not executable:
raise ValueError( """Null executable not allowed""" )
if os.path.basename( executable ) != executable:
raise ValueError( """Only base-name executables can be overridden""" )
description = {
'executable': executable,
'returncode': returncode,
'stdout': stdout,
'stderr': stderr,
'script': script,
}
self.scripts.setdefault(executable,[] ).append( description )
if self.bindir:
self.write_script( description )
return description
[docs] def remove( self, executable ):
"""Pop the current executable from our set of scripts
"""
try:
scripts = self.scripts[executable]
script = scripts.pop()
except (KeyError,IndexError), err:
return None
else:
if not scripts:
if self.bindir:
self.delete_script( executable )
del self.scripts[executable]
else:
if self.bindir:
self.write_script( scripts[-1] )
return script
script_template = """#! /usr/bin/env python
# mocked implementation of %(executable)s
import os,sys
stdout = %(stdout)r
stderr = %(stderr)r
if stdout:
sys.stdout.write( stdout )
if stderr:
sys.stderr.write( stderr )
sys.exit( %(returncode)s )
"""