You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					316 lines
				
				10 KiB
			
		
		
			
		
	
	
					316 lines
				
				10 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""
							 | 
						||
| 
								 | 
							
								exec_command
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Implements exec_command function that is (almost) equivalent to
							 | 
						||
| 
								 | 
							
								commands.getstatusoutput function but on NT, DOS systems the
							 | 
						||
| 
								 | 
							
								returned status is actually correct (though, the returned status
							 | 
						||
| 
								 | 
							
								values may be different by a factor). In addition, exec_command
							 | 
						||
| 
								 | 
							
								takes keyword arguments for (re-)defining environment variables.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Provides functions:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  exec_command  --- execute command in a specified directory and
							 | 
						||
| 
								 | 
							
								                    in the modified environment.
							 | 
						||
| 
								 | 
							
								  find_executable --- locate a command using info from environment
							 | 
						||
| 
								 | 
							
								                    variable PATH. Equivalent to posix `which`
							 | 
						||
| 
								 | 
							
								                    command.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Author: Pearu Peterson <pearu@cens.ioc.ee>
							 | 
						||
| 
								 | 
							
								Created: 11 January 2003
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Requires: Python 2.x
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Successfully tested on:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								========  ============  =================================================
							 | 
						||
| 
								 | 
							
								os.name   sys.platform  comments
							 | 
						||
| 
								 | 
							
								========  ============  =================================================
							 | 
						||
| 
								 | 
							
								posix     linux2        Debian (sid) Linux, Python 2.1.3+, 2.2.3+, 2.3.3
							 | 
						||
| 
								 | 
							
								                        PyCrust 0.9.3, Idle 1.0.2
							 | 
						||
| 
								 | 
							
								posix     linux2        Red Hat 9 Linux, Python 2.1.3, 2.2.2, 2.3.2
							 | 
						||
| 
								 | 
							
								posix     sunos5        SunOS 5.9, Python 2.2, 2.3.2
							 | 
						||
| 
								 | 
							
								posix     darwin        Darwin 7.2.0, Python 2.3
							 | 
						||
| 
								 | 
							
								nt        win32         Windows Me
							 | 
						||
| 
								 | 
							
								                        Python 2.3(EE), Idle 1.0, PyCrust 0.7.2
							 | 
						||
| 
								 | 
							
								                        Python 2.1.1 Idle 0.8
							 | 
						||
| 
								 | 
							
								nt        win32         Windows 98, Python 2.1.1. Idle 0.8
							 | 
						||
| 
								 | 
							
								nt        win32         Cygwin 98-4.10, Python 2.1.1(MSC) - echo tests
							 | 
						||
| 
								 | 
							
								                        fail i.e. redefining environment variables may
							 | 
						||
| 
								 | 
							
								                        not work. FIXED: don't use cygwin echo!
							 | 
						||
| 
								 | 
							
								                        Comment: also `cmd /c echo` will not work
							 | 
						||
| 
								 | 
							
								                        but redefining environment variables do work.
							 | 
						||
| 
								 | 
							
								posix     cygwin        Cygwin 98-4.10, Python 2.3.3(cygming special)
							 | 
						||
| 
								 | 
							
								nt        win32         Windows XP, Python 2.3.3
							 | 
						||
| 
								 | 
							
								========  ============  =================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Known bugs:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* Tests, that send messages to stderr, fail when executed from MSYS prompt
							 | 
						||
| 
								 | 
							
								  because the messages are lost at some point.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								__all__ = ['exec_command', 'find_executable']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								import subprocess
							 | 
						||
| 
								 | 
							
								import locale
							 | 
						||
| 
								 | 
							
								import warnings
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from numpy.distutils.misc_util import is_sequence, make_temp_file
							 | 
						||
| 
								 | 
							
								from numpy.distutils import log
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def filepath_from_subprocess_output(output):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Convert `bytes` in the encoding used by a subprocess into a filesystem-appropriate `str`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Inherited from `exec_command`, and possibly incorrect.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    mylocale = locale.getpreferredencoding(False)
							 | 
						||
| 
								 | 
							
								    if mylocale is None:
							 | 
						||
| 
								 | 
							
								        mylocale = 'ascii'
							 | 
						||
| 
								 | 
							
								    output = output.decode(mylocale, errors='replace')
							 | 
						||
| 
								 | 
							
								    output = output.replace('\r\n', '\n')
							 | 
						||
| 
								 | 
							
								    # Another historical oddity
							 | 
						||
| 
								 | 
							
								    if output[-1:] == '\n':
							 | 
						||
| 
								 | 
							
								        output = output[:-1]
							 | 
						||
| 
								 | 
							
								    return output
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def forward_bytes_to_stdout(val):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Forward bytes from a subprocess call to the console, without attempting to
							 | 
						||
| 
								 | 
							
								    decode them.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The assumption is that the subprocess call already returned bytes in
							 | 
						||
| 
								 | 
							
								    a suitable encoding.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if hasattr(sys.stdout, 'buffer'):
							 | 
						||
| 
								 | 
							
								        # use the underlying binary output if there is one
							 | 
						||
| 
								 | 
							
								        sys.stdout.buffer.write(val)
							 | 
						||
| 
								 | 
							
								    elif hasattr(sys.stdout, 'encoding'):
							 | 
						||
| 
								 | 
							
								        # round-trip the encoding if necessary
							 | 
						||
| 
								 | 
							
								        sys.stdout.write(val.decode(sys.stdout.encoding))
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        # make a best-guess at the encoding
							 | 
						||
| 
								 | 
							
								        sys.stdout.write(val.decode('utf8', errors='replace'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def temp_file_name():
							 | 
						||
| 
								 | 
							
								    # 2019-01-30, 1.17
							 | 
						||
| 
								 | 
							
								    warnings.warn('temp_file_name is deprecated since NumPy v1.17, use '
							 | 
						||
| 
								 | 
							
								                  'tempfile.mkstemp instead', DeprecationWarning, stacklevel=1)
							 | 
						||
| 
								 | 
							
								    fo, name = make_temp_file()
							 | 
						||
| 
								 | 
							
								    fo.close()
							 | 
						||
| 
								 | 
							
								    return name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_pythonexe():
							 | 
						||
| 
								 | 
							
								    pythonexe = sys.executable
							 | 
						||
| 
								 | 
							
								    if os.name in ['nt', 'dos']:
							 | 
						||
| 
								 | 
							
								        fdir, fn = os.path.split(pythonexe)
							 | 
						||
| 
								 | 
							
								        fn = fn.upper().replace('PYTHONW', 'PYTHON')
							 | 
						||
| 
								 | 
							
								        pythonexe = os.path.join(fdir, fn)
							 | 
						||
| 
								 | 
							
								        assert os.path.isfile(pythonexe), '%r is not a file' % (pythonexe,)
							 | 
						||
| 
								 | 
							
								    return pythonexe
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def find_executable(exe, path=None, _cache={}):
							 | 
						||
| 
								 | 
							
								    """Return full path of a executable or None.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Symbolic links are not followed.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    key = exe, path
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        return _cache[key]
							 | 
						||
| 
								 | 
							
								    except KeyError:
							 | 
						||
| 
								 | 
							
								        pass
							 | 
						||
| 
								 | 
							
								    log.debug('find_executable(%r)' % exe)
							 | 
						||
| 
								 | 
							
								    orig_exe = exe
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if path is None:
							 | 
						||
| 
								 | 
							
								        path = os.environ.get('PATH', os.defpath)
							 | 
						||
| 
								 | 
							
								    if os.name=='posix':
							 | 
						||
| 
								 | 
							
								        realpath = os.path.realpath
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        realpath = lambda a:a
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if exe.startswith('"'):
							 | 
						||
| 
								 | 
							
								        exe = exe[1:-1]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    suffixes = ['']
							 | 
						||
| 
								 | 
							
								    if os.name in ['nt', 'dos', 'os2']:
							 | 
						||
| 
								 | 
							
								        fn, ext = os.path.splitext(exe)
							 | 
						||
| 
								 | 
							
								        extra_suffixes = ['.exe', '.com', '.bat']
							 | 
						||
| 
								 | 
							
								        if ext.lower() not in extra_suffixes:
							 | 
						||
| 
								 | 
							
								            suffixes = extra_suffixes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if os.path.isabs(exe):
							 | 
						||
| 
								 | 
							
								        paths = ['']
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        paths = [ os.path.abspath(p) for p in path.split(os.pathsep) ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for path in paths:
							 | 
						||
| 
								 | 
							
								        fn = os.path.join(path, exe)
							 | 
						||
| 
								 | 
							
								        for s in suffixes:
							 | 
						||
| 
								 | 
							
								            f_ext = fn+s
							 | 
						||
| 
								 | 
							
								            if not os.path.islink(f_ext):
							 | 
						||
| 
								 | 
							
								                f_ext = realpath(f_ext)
							 | 
						||
| 
								 | 
							
								            if os.path.isfile(f_ext) and os.access(f_ext, os.X_OK):
							 | 
						||
| 
								 | 
							
								                log.info('Found executable %s' % f_ext)
							 | 
						||
| 
								 | 
							
								                _cache[key] = f_ext
							 | 
						||
| 
								 | 
							
								                return f_ext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    log.warn('Could not locate executable %s' % orig_exe)
							 | 
						||
| 
								 | 
							
								    return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								############################################################
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _preserve_environment( names ):
							 | 
						||
| 
								 | 
							
								    log.debug('_preserve_environment(%r)' % (names))
							 | 
						||
| 
								 | 
							
								    env = {name: os.environ.get(name) for name in names}
							 | 
						||
| 
								 | 
							
								    return env
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _update_environment( **env ):
							 | 
						||
| 
								 | 
							
								    log.debug('_update_environment(...)')
							 | 
						||
| 
								 | 
							
								    for name, value in env.items():
							 | 
						||
| 
								 | 
							
								        os.environ[name] = value or ''
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def exec_command(command, execute_in='', use_shell=None, use_tee=None,
							 | 
						||
| 
								 | 
							
								                 _with_python = 1, **env ):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Return (status,output) of executed command.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. deprecated:: 1.17
							 | 
						||
| 
								 | 
							
								        Use subprocess.Popen instead
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    command : str
							 | 
						||
| 
								 | 
							
								        A concatenated string of executable and arguments.
							 | 
						||
| 
								 | 
							
								    execute_in : str
							 | 
						||
| 
								 | 
							
								        Before running command ``cd execute_in`` and after ``cd -``.
							 | 
						||
| 
								 | 
							
								    use_shell : {bool, None}, optional
							 | 
						||
| 
								 | 
							
								        If True, execute ``sh -c command``. Default None (True)
							 | 
						||
| 
								 | 
							
								    use_tee : {bool, None}, optional
							 | 
						||
| 
								 | 
							
								        If True use tee. Default None (True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Returns
							 | 
						||
| 
								 | 
							
								    -------
							 | 
						||
| 
								 | 
							
								    res : str
							 | 
						||
| 
								 | 
							
								        Both stdout and stderr messages.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Notes
							 | 
						||
| 
								 | 
							
								    -----
							 | 
						||
| 
								 | 
							
								    On NT, DOS systems the returned status is correct for external commands.
							 | 
						||
| 
								 | 
							
								    Wild cards will not work for non-posix systems or when use_shell=0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # 2019-01-30, 1.17
							 | 
						||
| 
								 | 
							
								    warnings.warn('exec_command is deprecated since NumPy v1.17, use '
							 | 
						||
| 
								 | 
							
								                  'subprocess.Popen instead', DeprecationWarning, stacklevel=1)
							 | 
						||
| 
								 | 
							
								    log.debug('exec_command(%r,%s)' % (command,
							 | 
						||
| 
								 | 
							
								         ','.join(['%s=%r'%kv for kv in env.items()])))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if use_tee is None:
							 | 
						||
| 
								 | 
							
								        use_tee = os.name=='posix'
							 | 
						||
| 
								 | 
							
								    if use_shell is None:
							 | 
						||
| 
								 | 
							
								        use_shell = os.name=='posix'
							 | 
						||
| 
								 | 
							
								    execute_in = os.path.abspath(execute_in)
							 | 
						||
| 
								 | 
							
								    oldcwd = os.path.abspath(os.getcwd())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if __name__[-12:] == 'exec_command':
							 | 
						||
| 
								 | 
							
								        exec_dir = os.path.dirname(os.path.abspath(__file__))
							 | 
						||
| 
								 | 
							
								    elif os.path.isfile('exec_command.py'):
							 | 
						||
| 
								 | 
							
								        exec_dir = os.path.abspath('.')
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        exec_dir = os.path.abspath(sys.argv[0])
							 | 
						||
| 
								 | 
							
								        if os.path.isfile(exec_dir):
							 | 
						||
| 
								 | 
							
								            exec_dir = os.path.dirname(exec_dir)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if oldcwd!=execute_in:
							 | 
						||
| 
								 | 
							
								        os.chdir(execute_in)
							 | 
						||
| 
								 | 
							
								        log.debug('New cwd: %s' % execute_in)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        log.debug('Retaining cwd: %s' % oldcwd)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    oldenv = _preserve_environment( list(env.keys()) )
							 | 
						||
| 
								 | 
							
								    _update_environment( **env )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        st = _exec_command(command,
							 | 
						||
| 
								 | 
							
								                           use_shell=use_shell,
							 | 
						||
| 
								 | 
							
								                           use_tee=use_tee,
							 | 
						||
| 
								 | 
							
								                           **env)
							 | 
						||
| 
								 | 
							
								    finally:
							 | 
						||
| 
								 | 
							
								        if oldcwd!=execute_in:
							 | 
						||
| 
								 | 
							
								            os.chdir(oldcwd)
							 | 
						||
| 
								 | 
							
								            log.debug('Restored cwd to %s' % oldcwd)
							 | 
						||
| 
								 | 
							
								        _update_environment(**oldenv)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return st
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _exec_command(command, use_shell=None, use_tee = None, **env):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Internal workhorse for exec_command().
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if use_shell is None:
							 | 
						||
| 
								 | 
							
								        use_shell = os.name=='posix'
							 | 
						||
| 
								 | 
							
								    if use_tee is None:
							 | 
						||
| 
								 | 
							
								        use_tee = os.name=='posix'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if os.name == 'posix' and use_shell:
							 | 
						||
| 
								 | 
							
								        # On POSIX, subprocess always uses /bin/sh, override
							 | 
						||
| 
								 | 
							
								        sh = os.environ.get('SHELL', '/bin/sh')
							 | 
						||
| 
								 | 
							
								        if is_sequence(command):
							 | 
						||
| 
								 | 
							
								            command = [sh, '-c', ' '.join(command)]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            command = [sh, '-c', command]
							 | 
						||
| 
								 | 
							
								        use_shell = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elif os.name == 'nt' and is_sequence(command):
							 | 
						||
| 
								 | 
							
								        # On Windows, join the string for CreateProcess() ourselves as
							 | 
						||
| 
								 | 
							
								        # subprocess does it a bit differently
							 | 
						||
| 
								 | 
							
								        command = ' '.join(_quote_arg(arg) for arg in command)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Inherit environment by default
							 | 
						||
| 
								 | 
							
								    env = env or None
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        # text is set to False so that communicate()
							 | 
						||
| 
								 | 
							
								        # will return bytes. We need to decode the output ourselves
							 | 
						||
| 
								 | 
							
								        # so that Python will not raise a UnicodeDecodeError when
							 | 
						||
| 
								 | 
							
								        # it encounters an invalid character; rather, we simply replace it
							 | 
						||
| 
								 | 
							
								        proc = subprocess.Popen(command, shell=use_shell, env=env, text=False,
							 | 
						||
| 
								 | 
							
								                                stdout=subprocess.PIPE,
							 | 
						||
| 
								 | 
							
								                                stderr=subprocess.STDOUT)
							 | 
						||
| 
								 | 
							
								    except OSError:
							 | 
						||
| 
								 | 
							
								        # Return 127, as os.spawn*() and /bin/sh do
							 | 
						||
| 
								 | 
							
								        return 127, ''
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    text, err = proc.communicate()
							 | 
						||
| 
								 | 
							
								    mylocale = locale.getpreferredencoding(False)
							 | 
						||
| 
								 | 
							
								    if mylocale is None:
							 | 
						||
| 
								 | 
							
								        mylocale = 'ascii'
							 | 
						||
| 
								 | 
							
								    text = text.decode(mylocale, errors='replace')
							 | 
						||
| 
								 | 
							
								    text = text.replace('\r\n', '\n')
							 | 
						||
| 
								 | 
							
								    # Another historical oddity
							 | 
						||
| 
								 | 
							
								    if text[-1:] == '\n':
							 | 
						||
| 
								 | 
							
								        text = text[:-1]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if use_tee and text:
							 | 
						||
| 
								 | 
							
								        print(text)
							 | 
						||
| 
								 | 
							
								    return proc.returncode, text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _quote_arg(arg):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Quote the argument for safe use in a shell command line.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # If there is a quote in the string, assume relevants parts of the
							 | 
						||
| 
								 | 
							
								    # string are already quoted (e.g. '-I"C:\\Program Files\\..."')
							 | 
						||
| 
								 | 
							
								    if '"' not in arg and ' ' in arg:
							 | 
						||
| 
								 | 
							
								        return '"%s"' % arg
							 | 
						||
| 
								 | 
							
								    return arg
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								############################################################
							 |