352 lines
9.5 KiB
Python
352 lines
9.5 KiB
Python
# -*- coding: ascii -*-
|
|
#
|
|
# Copyright 2007 - 2013
|
|
# Andr\xe9 Malo or his licensors, as applicable
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""
|
|
=================
|
|
Shell utilities
|
|
=================
|
|
|
|
Shell utilities.
|
|
"""
|
|
__author__ = "Andr\xe9 Malo"
|
|
__docformat__ = "restructuredtext en"
|
|
|
|
import errno as _errno
|
|
import fnmatch as _fnmatch
|
|
import os as _os
|
|
import shutil as _shutil
|
|
import subprocess as _subprocess
|
|
import sys as _sys
|
|
import tempfile as _tempfile
|
|
|
|
cwd = _os.path.dirname(_os.path.abspath(_sys.argv[0]))
|
|
|
|
class ExitError(RuntimeError):
|
|
""" Exit error """
|
|
def __init__(self, code):
|
|
RuntimeError.__init__(self, code)
|
|
self.code = code
|
|
self.signal = None
|
|
|
|
|
|
class SignalError(ExitError):
|
|
""" Signal error """
|
|
def __init__(self, code, signal):
|
|
ExitError.__init__(self, code)
|
|
import signal as _signal
|
|
self.signal = signal
|
|
for key, val in vars(_signal).items():
|
|
if key.startswith('SIG') and not key.startswith('SIG_'):
|
|
if val == signal:
|
|
self.signalstr = key[3:]
|
|
break
|
|
else:
|
|
self.signalstr = '%04d' % signal
|
|
|
|
|
|
def native(path):
|
|
""" Convert slash path to native """
|
|
path = _os.path.sep.join(path.split('/'))
|
|
return _os.path.normpath(_os.path.join(cwd, path))
|
|
|
|
|
|
def cp(src, dest):
|
|
""" Copy src to dest """
|
|
_shutil.copy2(native(src), native(dest))
|
|
|
|
|
|
def cp_r(src, dest):
|
|
""" Copy -r src to dest """
|
|
_shutil.copytree(native(src), native(dest))
|
|
|
|
|
|
def rm(dest):
|
|
""" Remove a file """
|
|
try:
|
|
_os.unlink(native(dest))
|
|
except OSError as e:
|
|
if _errno.ENOENT != e.errno:
|
|
raise
|
|
|
|
def rm_rf(dest):
|
|
""" Remove a tree """
|
|
dest = native(dest)
|
|
if _os.path.exists(dest):
|
|
for path in files(dest, '*'):
|
|
_os.chmod(native(path), 0o644)
|
|
_shutil.rmtree(dest)
|
|
|
|
|
|
mkstemp = _tempfile.mkstemp
|
|
|
|
|
|
def _pipespawn(argv, env):
|
|
""" Pipe spawn """
|
|
# pylint: disable = R0912
|
|
import pickle as _pickle
|
|
fd, name = mkstemp('.py')
|
|
try:
|
|
_os.write(fd, ((r"""
|
|
import os
|
|
import pickle
|
|
import subprocess
|
|
import sys
|
|
|
|
argv = pickle.loads(%(argv)s)
|
|
env = pickle.loads(%(env)s)
|
|
if 'X_JYTHON_WA_PATH' in env:
|
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
|
|
|
p = subprocess.Popen(argv, env=env)
|
|
result = p.wait()
|
|
if result < 0:
|
|
print("\n%%d 1" %% (-result))
|
|
sys.exit(2)
|
|
if result == 0:
|
|
sys.exit(0)
|
|
print("\n%%d" %% (result & 7,))
|
|
sys.exit(3)
|
|
""".strip() + "\n") % {
|
|
'argv': repr(_pickle.dumps(argv)),
|
|
'env': repr(_pickle.dumps(dict(env))),
|
|
}).encode('utf-8'))
|
|
fd, _ = None, _os.close(fd)
|
|
if _sys.platform == 'win32':
|
|
argv = []
|
|
for arg in [_sys.executable, name]:
|
|
if ' ' in arg or arg.startswith('"'):
|
|
arg = '"%s"' % arg.replace('"', '\\"')
|
|
argv.append(arg)
|
|
argv = ' '.join(argv)
|
|
shell = True
|
|
close_fds = False
|
|
else:
|
|
argv = [_sys.executable, name]
|
|
shell = False
|
|
close_fds = True
|
|
|
|
res = 0
|
|
if 'X_JYTHON_WA_PATH' in env:
|
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
|
|
|
proc = _subprocess.Popen(argv,
|
|
shell=shell,
|
|
stdin=_subprocess.PIPE,
|
|
stdout=_subprocess.PIPE,
|
|
close_fds=close_fds,
|
|
env=env,
|
|
)
|
|
try:
|
|
proc.stdin.close()
|
|
result = proc.stdout.read()
|
|
finally:
|
|
res = proc.wait()
|
|
if res != 0:
|
|
if res == 2:
|
|
signal, code = list(map(int, result.splitlines()[-1].split()))
|
|
raise SignalError(code, signal)
|
|
elif res == 3:
|
|
code = int(result.splitlines()[-1].strip())
|
|
raise ExitError(code)
|
|
raise ExitError(res)
|
|
|
|
return result.decode('latin-1')
|
|
finally:
|
|
try:
|
|
if fd is not None:
|
|
_os.close(fd)
|
|
finally:
|
|
_os.unlink(name)
|
|
|
|
|
|
def _filepipespawn(infile, outfile, argv, env):
|
|
""" File Pipe spawn """
|
|
import pickle as _pickle
|
|
fd, name = mkstemp('.py')
|
|
try:
|
|
_os.write(fd, (("""
|
|
import os
|
|
import pickle
|
|
import sys
|
|
|
|
infile = pickle.loads(%(infile)s)
|
|
outfile = pickle.loads(%(outfile)s)
|
|
argv = pickle.loads(%(argv)s)
|
|
env = pickle.loads(%(env)s)
|
|
|
|
if infile is not None:
|
|
infile = open(infile, 'rb')
|
|
os.dup2(infile.fileno(), 0)
|
|
infile.close()
|
|
if outfile is not None:
|
|
outfile = open(outfile, 'wb')
|
|
os.dup2(outfile.fileno(), 1)
|
|
outfile.close()
|
|
|
|
pid = os.spawnve(os.P_NOWAIT, argv[0], argv, env)
|
|
result = os.waitpid(pid, 0)[1]
|
|
sys.exit(result & 7)
|
|
""".strip() + "\n") % {
|
|
'infile': repr(_pickle.dumps(_os.path.abspath(infile))),
|
|
'outfile': repr(_pickle.dumps(_os.path.abspath(outfile))),
|
|
'argv': repr(_pickle.dumps(argv)),
|
|
'env': repr(_pickle.dumps(env)),
|
|
}).encode('utf-8'))
|
|
fd, _ = None, _os.close(fd)
|
|
if _sys.platform == 'win32':
|
|
argv = []
|
|
for arg in [_sys.executable, name]:
|
|
if ' ' in arg or arg.startswith('"'):
|
|
arg = '"%s"' % arg.replace('"', '\\"')
|
|
argv.append(arg)
|
|
argv = ' '.join(argv)
|
|
close_fds = False
|
|
shell = True
|
|
else:
|
|
argv = [_sys.executable, name]
|
|
close_fds = True
|
|
shell = False
|
|
|
|
p = _subprocess.Popen(
|
|
argv, env=env, shell=shell, close_fds=close_fds
|
|
)
|
|
return p.wait()
|
|
finally:
|
|
try:
|
|
if fd is not None:
|
|
_os.close(fd)
|
|
finally:
|
|
_os.unlink(name)
|
|
|
|
|
|
def spawn(*argv, **kwargs):
|
|
""" Spawn a process """
|
|
if _sys.platform == 'win32':
|
|
newargv = []
|
|
for arg in argv:
|
|
if not arg or ' ' in arg or arg.startswith('"'):
|
|
arg = '"%s"' % arg.replace('"', '\\"')
|
|
newargv.append(arg)
|
|
argv = newargv
|
|
close_fds = False
|
|
shell = True
|
|
else:
|
|
close_fds = True
|
|
shell = False
|
|
|
|
env = kwargs.get('env')
|
|
if env is None:
|
|
env = dict(_os.environ)
|
|
if 'X_JYTHON_WA_PATH' in env:
|
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
|
|
|
echo = kwargs.get('echo')
|
|
if echo:
|
|
print(' '.join(argv))
|
|
filepipe = kwargs.get('filepipe')
|
|
if filepipe:
|
|
return _filepipespawn(
|
|
kwargs.get('stdin'), kwargs.get('stdout'), argv, env
|
|
)
|
|
pipe = kwargs.get('stdout')
|
|
if pipe:
|
|
return _pipespawn(argv, env)
|
|
|
|
p = _subprocess.Popen(argv, env=env, shell=shell, close_fds=close_fds)
|
|
return p.wait()
|
|
|
|
|
|
walk = _os.walk
|
|
|
|
|
|
def files(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
|
""" Determine a filelist """
|
|
for dirpath, dirnames, filenames in walk(native(base)):
|
|
for item in prune:
|
|
if item in dirnames:
|
|
dirnames.remove(item)
|
|
|
|
filenames.sort()
|
|
for name in _fnmatch.filter(filenames, wildcard):
|
|
dest = _os.path.join(dirpath, name)
|
|
if dest.startswith(cwd):
|
|
dest = dest.replace(cwd, '', 1)
|
|
aslist = []
|
|
head, tail = _os.path.split(dest)
|
|
while tail:
|
|
aslist.append(tail)
|
|
head, tail = _os.path.split(head)
|
|
aslist.reverse()
|
|
dest = '/'.join(aslist)
|
|
yield dest
|
|
|
|
if not recursive:
|
|
break
|
|
dirnames.sort()
|
|
|
|
|
|
def dirs(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
|
""" Determine a filelist """
|
|
for dirpath, dirnames, filenames in walk(native(base)):
|
|
for item in prune:
|
|
if item in dirnames:
|
|
dirnames.remove(item)
|
|
|
|
dirnames.sort()
|
|
for name in _fnmatch.filter(dirnames, wildcard):
|
|
dest = _os.path.join(dirpath, name)
|
|
if dest.startswith(cwd):
|
|
dest = dest.replace(cwd, '', 1)
|
|
aslist = []
|
|
head, tail = _os.path.split(dest)
|
|
while tail:
|
|
aslist.append(tail)
|
|
head, tail = _os.path.split(head)
|
|
aslist.reverse()
|
|
dest = '/'.join(aslist)
|
|
yield dest
|
|
|
|
if not recursive:
|
|
break
|
|
|
|
|
|
def frompath(executable):
|
|
""" Find executable in PATH """
|
|
# Based on distutils.spawn.find_executable.
|
|
path = _os.environ.get('PATH', '')
|
|
paths = [
|
|
_os.path.expanduser(item)
|
|
for item in path.split(_os.pathsep)
|
|
]
|
|
ext = _os.path.splitext(executable)[1]
|
|
exts = ['']
|
|
if _sys.platform == 'win32' or _os.name == 'os2':
|
|
eext = ['.exe', '.bat', '.py']
|
|
if ext not in eext:
|
|
exts.extend(eext)
|
|
|
|
for ext in exts:
|
|
if not _os.path.isfile(executable + ext):
|
|
for path in paths:
|
|
fname = _os.path.join(path, executable + ext)
|
|
if _os.path.isfile(fname):
|
|
# the file exists, we have a shot at spawn working
|
|
return fname
|
|
else:
|
|
return executable + ext
|
|
|
|
return None
|