Install missing dependencies when figuring out declared dependencies.
This commit is contained in:
parent
c9e2db373f
commit
3aeb984147
6 changed files with 148 additions and 64 deletions
|
@ -47,12 +47,12 @@ def get_necessary_declared_requirements(resolver, requirements, stages):
|
||||||
return missing
|
return missing
|
||||||
|
|
||||||
|
|
||||||
def install_necessary_declared_requirements(session, resolver, buildsystems, stages, explain=False):
|
def install_necessary_declared_requirements(session, resolver, fixers, buildsystems, stages, explain=False):
|
||||||
relevant = []
|
relevant = []
|
||||||
declared_reqs = []
|
declared_reqs = []
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
try:
|
try:
|
||||||
declared_reqs.extend(buildsystem.get_declared_dependencies())
|
declared_reqs.extend(buildsystem.get_declared_dependencies(session, fixers))
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Unable to determine declared dependencies from %r", buildsystem
|
"Unable to determine declared dependencies from %r", buildsystem
|
||||||
|
@ -169,17 +169,17 @@ def main(): # noqa: C901
|
||||||
bss = list(detect_buildsystems(args.directory))
|
bss = list(detect_buildsystems(args.directory))
|
||||||
logging.info(
|
logging.info(
|
||||||
"Detected buildsystems: %s", ', '.join(map(str, bss)))
|
"Detected buildsystems: %s", ', '.join(map(str, bss)))
|
||||||
|
fixers = determine_fixers(session, resolver, explain=args.explain)
|
||||||
if not args.ignore_declared_dependencies:
|
if not args.ignore_declared_dependencies:
|
||||||
stages = STAGE_MAP[args.subcommand]
|
stages = STAGE_MAP[args.subcommand]
|
||||||
if stages:
|
if stages:
|
||||||
logging.info("Checking that declared requirements are present")
|
logging.info("Checking that declared requirements are present")
|
||||||
try:
|
try:
|
||||||
install_necessary_declared_requirements(
|
install_necessary_declared_requirements(
|
||||||
session, resolver, bss, stages, explain=args.explain)
|
session, resolver, fixers, bss, stages, explain=args.explain)
|
||||||
except ExplainInstall as e:
|
except ExplainInstall as e:
|
||||||
display_explain_commands(e.commands)
|
display_explain_commands(e.commands)
|
||||||
return 1
|
return 1
|
||||||
fixers = determine_fixers(session, resolver, explain=args.explain)
|
|
||||||
if args.subcommand == "dist":
|
if args.subcommand == "dist":
|
||||||
from .dist import run_dist
|
from .dist import run_dist
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ class BuildSystem(object):
|
||||||
def install(self, session, resolver, fixers, install_target):
|
def install(self, session, resolver, fixers, install_target):
|
||||||
raise NotImplementedError(self.install)
|
raise NotImplementedError(self.install)
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
raise NotImplementedError(self.get_declared_dependencies)
|
raise NotImplementedError(self.get_declared_dependencies)
|
||||||
|
|
||||||
def get_declared_outputs(self):
|
def get_declared_outputs(self, session, fixers=None):
|
||||||
raise NotImplementedError(self.get_declared_outputs)
|
raise NotImplementedError(self.get_declared_outputs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -124,7 +124,6 @@ class Pear(BuildSystem):
|
||||||
# run_setup, but setting __name__
|
# run_setup, but setting __name__
|
||||||
# Imported from Python's distutils.core, Copyright (C) PSF
|
# Imported from Python's distutils.core, Copyright (C) PSF
|
||||||
|
|
||||||
|
|
||||||
def run_setup(script_name, script_args=None, stop_after="run"):
|
def run_setup(script_name, script_args=None, stop_after="run"):
|
||||||
from distutils import core
|
from distutils import core
|
||||||
import sys
|
import sys
|
||||||
|
@ -154,36 +153,64 @@ def run_setup(script_name, script_args=None, stop_after="run"):
|
||||||
# (ie. error)?
|
# (ie. error)?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if core._setup_distribution is None:
|
|
||||||
raise RuntimeError(
|
|
||||||
(
|
|
||||||
"'distutils.core.setup()' was never called -- "
|
|
||||||
"perhaps '%s' is not a Distutils setup script?"
|
|
||||||
)
|
|
||||||
% script_name
|
|
||||||
)
|
|
||||||
|
|
||||||
return core._setup_distribution
|
return core._setup_distribution
|
||||||
|
|
||||||
|
|
||||||
|
_setup_wrapper = """\
|
||||||
|
from distutils import core
|
||||||
|
import sys
|
||||||
|
|
||||||
|
script_name = %(script_name)s
|
||||||
|
|
||||||
|
save_argv = sys.argv.copy()
|
||||||
|
g = {"__file__": script_name, "__name__": "__main__"}
|
||||||
|
try:
|
||||||
|
core._setup_stop_after = "init"
|
||||||
|
sys.argv[0] = script_name
|
||||||
|
with open(script_name, "rb") as f:
|
||||||
|
exec(f.read(), g)
|
||||||
|
except SystemExit:
|
||||||
|
# Hmm, should we do something if exiting with a non-zero code
|
||||||
|
# (ie. error)?
|
||||||
|
pass
|
||||||
|
|
||||||
|
if core._setup_distribution is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
(
|
||||||
|
"'distutils.core.setup()' was never called -- "
|
||||||
|
"perhaps '%s' is not a Distutils setup script?"
|
||||||
|
)
|
||||||
|
% script_name
|
||||||
|
)
|
||||||
|
|
||||||
|
d = core._setup_distribution
|
||||||
|
r = {
|
||||||
|
'setup_requires': getattr(d, "setup_requires", []),
|
||||||
|
'install_requires': getattr(d, "install_requires", []),
|
||||||
|
'tests_require': getattr(d, "tests_require", []),
|
||||||
|
'scripts': getattr(d, "scripts", []) or [],
|
||||||
|
'entry_points': getattr(d, "entry_points", None) or {},
|
||||||
|
'packages': getattr(d, "packages", []) or [],
|
||||||
|
'requires': d.get_requires() or [],
|
||||||
|
}
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
with open(%(output_path)s, 'w') as f:
|
||||||
|
json.dump(r, f)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class SetupPy(BuildSystem):
|
class SetupPy(BuildSystem):
|
||||||
|
|
||||||
name = "setup.py"
|
name = "setup.py"
|
||||||
|
DEFAULT_PYTHON = 'python3'
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
if os.path.exists(os.path.join(self.path, 'setup.py')):
|
if os.path.exists(os.path.join(self.path, 'setup.py')):
|
||||||
self.has_setup_py = True
|
self.has_setup_py = True
|
||||||
# TODO(jelmer): Perhaps run this in session, so we can install
|
|
||||||
# missing dependencies?
|
|
||||||
try:
|
|
||||||
self.distribution = run_setup(os.path.abspath(os.path.join(self.path, 'setup.py')), stop_after="init")
|
|
||||||
except RuntimeError as e:
|
|
||||||
logging.warning("Unable to load setup.py metadata: %s", e)
|
|
||||||
self.distribution = None
|
|
||||||
else:
|
else:
|
||||||
self.has_setup_py = False
|
self.has_setup_py = False
|
||||||
self.distribution = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.pyproject = self.load_toml()
|
self.pyproject = self.load_toml()
|
||||||
|
@ -196,28 +223,79 @@ class SetupPy(BuildSystem):
|
||||||
with open(os.path.join(self.path, "pyproject.toml"), "r") as pf:
|
with open(os.path.join(self.path, "pyproject.toml"), "r") as pf:
|
||||||
return toml.load(pf)
|
return toml.load(pf)
|
||||||
|
|
||||||
|
def _extract_setup(self, session=None, fixers=None):
|
||||||
|
if session is None:
|
||||||
|
return self._extract_setup_direct()
|
||||||
|
else:
|
||||||
|
return self._extract_setup_in_session(session, fixers)
|
||||||
|
|
||||||
|
def _extract_setup_direct(self):
|
||||||
|
p = os.path.join(self.path, 'setup.py')
|
||||||
|
try:
|
||||||
|
d = run_setup(os.path.abspath(p), stop_after="init")
|
||||||
|
except RuntimeError as e:
|
||||||
|
logging.warning("Unable to load setup.py metadata: %s", e)
|
||||||
|
return None
|
||||||
|
if d is None:
|
||||||
|
logging.warning(
|
||||||
|
"'distutils.core.setup()' was never called -- "
|
||||||
|
"perhaps '%s' is not a Distutils setup script?" % os.path.basename(p))
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'setup_requires': getattr(d, "setup_requires", []),
|
||||||
|
'install_requires': getattr(d, "install_requires", []),
|
||||||
|
'tests_require': getattr(d, "tests_require", []),
|
||||||
|
'scripts': getattr(d, "scripts", []),
|
||||||
|
'entry_points': getattr(d, "entry_points", None) or {},
|
||||||
|
'packages': getattr(d, "packages", []),
|
||||||
|
'requires': d.get_requires() or [],
|
||||||
|
}
|
||||||
|
|
||||||
|
def _extract_setup_in_session(self, session, fixers=None):
|
||||||
|
import tempfile
|
||||||
|
import json
|
||||||
|
interpreter = shebang_binary(os.path.join(self.path, "setup.py"))
|
||||||
|
if interpreter is None:
|
||||||
|
interpreter = self.DEFAULT_PYTHON
|
||||||
|
output_f = tempfile.NamedTemporaryFile(
|
||||||
|
dir=os.path.join(session.location, 'tmp'), mode='w+t')
|
||||||
|
with output_f:
|
||||||
|
# TODO(jelmer): Perhaps run this in session, so we can install
|
||||||
|
# missing dependencies?
|
||||||
|
argv = [interpreter, "-c",
|
||||||
|
_setup_wrapper
|
||||||
|
.replace('%(script_name)s', '"setup.py"')
|
||||||
|
.replace('%(output_path)s',
|
||||||
|
'"/' + os.path.relpath(output_f.name, session.location) +
|
||||||
|
'"')]
|
||||||
|
try:
|
||||||
|
if fixers is not None:
|
||||||
|
run_with_build_fixers(session, argv, fixers)
|
||||||
|
else:
|
||||||
|
session.check_call(argv, close_fds=False)
|
||||||
|
except RuntimeError as e:
|
||||||
|
logging.warning("Unable to load setup.py metadata: %s", e)
|
||||||
|
return None
|
||||||
|
output_f.seek(0)
|
||||||
|
return json.load(output_f)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.path)
|
return "%s(%r)" % (type(self).__name__, self.path)
|
||||||
|
|
||||||
def setup(self, resolver):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test(self, session, resolver, fixers):
|
def test(self, session, resolver, fixers):
|
||||||
self.setup(resolver)
|
|
||||||
if self.has_setup_py:
|
if self.has_setup_py:
|
||||||
self._run_setup(session, resolver, ["test"], fixers)
|
self._run_setup(session, resolver, ["test"], fixers)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def build(self, session, resolver, fixers):
|
def build(self, session, resolver, fixers):
|
||||||
self.setup(resolver)
|
|
||||||
if self.has_setup_py:
|
if self.has_setup_py:
|
||||||
self._run_setup(session, resolver, ["build"], fixers)
|
self._run_setup(session, resolver, ["build"], fixers)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def dist(self, session, resolver, fixers, quiet=False):
|
def dist(self, session, resolver, fixers, quiet=False):
|
||||||
self.setup(resolver)
|
|
||||||
if self.has_setup_py:
|
if self.has_setup_py:
|
||||||
preargs = []
|
preargs = []
|
||||||
if quiet:
|
if quiet:
|
||||||
|
@ -234,14 +312,12 @@ class SetupPy(BuildSystem):
|
||||||
raise AssertionError("no supported section in pyproject.toml")
|
raise AssertionError("no supported section in pyproject.toml")
|
||||||
|
|
||||||
def clean(self, session, resolver, fixers):
|
def clean(self, session, resolver, fixers):
|
||||||
self.setup(resolver)
|
|
||||||
if self.has_setup_py:
|
if self.has_setup_py:
|
||||||
self._run_setup(session, resolver, ["clean"], fixers)
|
self._run_setup(session, resolver, ["clean"], fixers)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def install(self, session, resolver, fixers, install_target):
|
def install(self, session, resolver, fixers, install_target):
|
||||||
self.setup(resolver)
|
|
||||||
if self.has_setup_py:
|
if self.has_setup_py:
|
||||||
extra_args = []
|
extra_args = []
|
||||||
if install_target.user:
|
if install_target.user:
|
||||||
|
@ -253,44 +329,50 @@ class SetupPy(BuildSystem):
|
||||||
def _run_setup(self, session, resolver, args, fixers):
|
def _run_setup(self, session, resolver, args, fixers):
|
||||||
interpreter = shebang_binary(os.path.join(self.path, 'setup.py'))
|
interpreter = shebang_binary(os.path.join(self.path, 'setup.py'))
|
||||||
if interpreter is not None:
|
if interpreter is not None:
|
||||||
resolver.install([BinaryRequirement(interpreter)])
|
|
||||||
run_with_build_fixers(session, ["./setup.py"] + args, fixers)
|
run_with_build_fixers(session, ["./setup.py"] + args, fixers)
|
||||||
else:
|
else:
|
||||||
# Just assume it's Python 3
|
# Just assume it's Python 3
|
||||||
resolver.install([BinaryRequirement("python3")])
|
run_with_build_fixers(session, [self.DEFAULT_PYTHON, "./setup.py"] + args, fixers)
|
||||||
run_with_build_fixers(session, ["python3", "./setup.py"] + args, fixers)
|
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
if self.distribution is None:
|
distribution = self._extract_setup(session, fixers)
|
||||||
|
if distribution is None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
for require in self.distribution.get_requires():
|
for require in distribution['requires']:
|
||||||
yield "core", PythonPackageRequirement.from_requirement_str(require)
|
yield "core", PythonPackageRequirement.from_requirement_str(require)
|
||||||
# Not present for distutils-only packages
|
# Not present for distutils-only packages
|
||||||
if getattr(self.distribution, "setup_requires", []):
|
for require in distribution['setup_requires']:
|
||||||
for require in self.distribution.setup_requires:
|
yield "build", PythonPackageRequirement.from_requirement_str(require)
|
||||||
yield "build", PythonPackageRequirement.from_requirement_str(require)
|
|
||||||
# Not present for distutils-only packages
|
# Not present for distutils-only packages
|
||||||
if getattr(self.distribution, "install_requires", []):
|
for require in distribution['install_requires']:
|
||||||
for require in self.distribution.install_requires:
|
yield "core", PythonPackageRequirement.from_requirement_str(require)
|
||||||
yield "core", PythonPackageRequirement.from_requirement_str(require)
|
|
||||||
# Not present for distutils-only packages
|
# Not present for distutils-only packages
|
||||||
if getattr(self.distribution, "tests_require", []):
|
for require in distribution['tests_require']:
|
||||||
for require in self.distribution.tests_require:
|
yield "test", PythonPackageRequirement.from_requirement_str(require)
|
||||||
yield "test", PythonPackageRequirement.from_requirement_str(require)
|
|
||||||
if self.pyproject:
|
if self.pyproject:
|
||||||
if "build-system" in self.pyproject:
|
if "build-system" in self.pyproject:
|
||||||
for require in self.pyproject['build-system'].get("requires", []):
|
for require in self.pyproject['build-system'].get("requires", []):
|
||||||
yield "build", PythonPackageRequirement.from_requirement_str(require)
|
yield "build", PythonPackageRequirement.from_requirement_str(require)
|
||||||
|
|
||||||
def get_declared_outputs(self):
|
def get_declared_outputs(self, session, fixers=None):
|
||||||
if self.distribution is None:
|
distribution = self._extract_setup(session, fixers)
|
||||||
|
if distribution is None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
for script in self.distribution.scripts or []:
|
for script in distribution['scripts']:
|
||||||
yield BinaryOutput(os.path.basename(script))
|
yield BinaryOutput(os.path.basename(script))
|
||||||
entry_points = getattr(self.distribution, "entry_points", None) or {}
|
for script in distribution["entry_points"].get("console_scripts", []):
|
||||||
for script in entry_points.get("console_scripts", []):
|
|
||||||
yield BinaryOutput(script.split("=")[0])
|
yield BinaryOutput(script.split("=")[0])
|
||||||
for package in self.distribution.packages or []:
|
packages = set()
|
||||||
|
for package in sorted(distribution['packages']):
|
||||||
|
pts = package.split('.')
|
||||||
|
b = []
|
||||||
|
for e in pts:
|
||||||
|
b.append(e)
|
||||||
|
if '.'.join(b) in packages:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
packages.add(package)
|
||||||
|
for package in packages:
|
||||||
yield PythonPackageOutput(package, python_version="cpython3")
|
yield PythonPackageOutput(package, python_version="cpython3")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -438,7 +520,7 @@ class Npm(BuildSystem):
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
self.package = json.load(f)
|
self.package = json.load(f)
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
if "devDependencies" in self.package:
|
if "devDependencies" in self.package:
|
||||||
for name, unused_version in self.package["devDependencies"].items():
|
for name, unused_version in self.package["devDependencies"].items():
|
||||||
# TODO(jelmer): Look at version
|
# TODO(jelmer): Look at version
|
||||||
|
@ -555,9 +637,8 @@ class DistInkt(BuildSystem):
|
||||||
):
|
):
|
||||||
return cls(os.path.join(path, "dist.ini"))
|
return cls(os.path.join(path, "dist.ini"))
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
import subprocess
|
out = session.check_output(["dzil", "authordeps"])
|
||||||
out = subprocess.check_output(["dzil", "authordeps"])
|
|
||||||
for entry in out.splitlines():
|
for entry in out.splitlines():
|
||||||
yield "build", PerlModuleRequirement(entry.decode())
|
yield "build", PerlModuleRequirement(entry.decode())
|
||||||
|
|
||||||
|
@ -682,7 +763,7 @@ class Make(BuildSystem):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
# TODO(jelmer): Split out the perl-specific stuff?
|
# TODO(jelmer): Split out the perl-specific stuff?
|
||||||
if os.path.exists(os.path.join(self.path, "META.yml")):
|
if os.path.exists(os.path.join(self.path, "META.yml")):
|
||||||
# See http://module-build.sourceforge.net/META-spec-v1.4.html for
|
# See http://module-build.sourceforge.net/META-spec-v1.4.html for
|
||||||
|
@ -740,7 +821,7 @@ class Cargo(BuildSystem):
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
self.cargo = load(f)
|
self.cargo = load(f)
|
||||||
|
|
||||||
def get_declared_dependencies(self):
|
def get_declared_dependencies(self, session, fixers=None):
|
||||||
if "dependencies" in self.cargo:
|
if "dependencies" in self.cargo:
|
||||||
for name, details in self.cargo["dependencies"].items():
|
for name, details in self.cargo["dependencies"].items():
|
||||||
if isinstance(details, str):
|
if isinstance(details, str):
|
||||||
|
|
|
@ -21,7 +21,7 @@ def run_info(session, buildsystems):
|
||||||
print("%r:" % buildsystem)
|
print("%r:" % buildsystem)
|
||||||
deps = {}
|
deps = {}
|
||||||
try:
|
try:
|
||||||
for kind, dep in buildsystem.get_declared_dependencies():
|
for kind, dep in buildsystem.get_declared_dependencies(session):
|
||||||
deps.setdefault(kind, []).append(dep)
|
deps.setdefault(kind, []).append(dep)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print(
|
print(
|
||||||
|
@ -35,7 +35,7 @@ def run_info(session, buildsystems):
|
||||||
print("\t\t\t%s" % dep)
|
print("\t\t\t%s" % dep)
|
||||||
print("")
|
print("")
|
||||||
try:
|
try:
|
||||||
outputs = list(buildsystem.get_declared_outputs())
|
outputs = list(buildsystem.get_declared_outputs(session))
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print("\tUnable to detect declared outputs for this type of build system")
|
print("\tUnable to detect declared outputs for this type of build system")
|
||||||
outputs = []
|
outputs = []
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Session(object):
|
||||||
cwd: Optional[str] = None,
|
cwd: Optional[str] = None,
|
||||||
user: Optional[str] = None,
|
user: Optional[str] = None,
|
||||||
env: Optional[Dict[str, str]] = None,
|
env: Optional[Dict[str, str]] = None,
|
||||||
|
close_fds: bool = True
|
||||||
):
|
):
|
||||||
raise NotImplementedError(self.check_call)
|
raise NotImplementedError(self.check_call)
|
||||||
|
|
||||||
|
|
|
@ -56,9 +56,10 @@ class PlainSession(Session):
|
||||||
self, argv: List[str],
|
self, argv: List[str],
|
||||||
cwd: Optional[str] = None,
|
cwd: Optional[str] = None,
|
||||||
user: Optional[str] = None,
|
user: Optional[str] = None,
|
||||||
env: Optional[Dict[str, str]] = None):
|
env: Optional[Dict[str, str]] = None,
|
||||||
|
close_fds: bool = True):
|
||||||
argv = self._prepend_user(user, argv)
|
argv = self._prepend_user(user, argv)
|
||||||
return subprocess.check_call(argv, cwd=cwd, env=env)
|
return subprocess.check_call(argv, cwd=cwd, env=env, close_fds=close_fds)
|
||||||
|
|
||||||
def check_output(
|
def check_output(
|
||||||
self, argv: List[str],
|
self, argv: List[str],
|
||||||
|
|
|
@ -114,9 +114,10 @@ class SchrootSession(Session):
|
||||||
cwd: Optional[str] = None,
|
cwd: Optional[str] = None,
|
||||||
user: Optional[str] = None,
|
user: Optional[str] = None,
|
||||||
env: Optional[Dict[str, str]] = None,
|
env: Optional[Dict[str, str]] = None,
|
||||||
|
close_fds: bool = True
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(self._run_argv(argv, cwd, user, env=env))
|
subprocess.check_call(self._run_argv(argv, cwd, user, env=env), close_fds=close_fds)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise subprocess.CalledProcessError(e.returncode, argv)
|
raise subprocess.CalledProcessError(e.returncode, argv)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue