spack python: add -m option to run modules as scripts
It's often useful to run a module with `python -m`, e.g.:
python -m pyinstrument script.py
Running a python script this way was hard, though, as `spack python` did
not have a similar `-m` option. This PR adds a `-m` option to `spack
python` so that we can do things like this:
spack python -m pyinstrument ./test.py
This makes it easy to write a script that uses a small part of Spack and
then profile it. Previously thee easiest way to do this was to write a
custom Spack command, which is often overkill.
This commit is contained in:
@@ -8,6 +8,9 @@
|
|||||||
import code
|
import code
|
||||||
import argparse
|
import argparse
|
||||||
import platform
|
import platform
|
||||||
|
import runpy
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
|
||||||
@@ -19,12 +22,23 @@
|
|||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-c', dest='python_command', help='command to execute')
|
'-c', dest='python_command', help='command to execute')
|
||||||
|
subparser.add_argument(
|
||||||
|
'-m', dest='module', action='store',
|
||||||
|
help='run library module as a script')
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'python_args', nargs=argparse.REMAINDER,
|
'python_args', nargs=argparse.REMAINDER,
|
||||||
help="file to run plus arguments")
|
help="file to run plus arguments")
|
||||||
|
|
||||||
|
|
||||||
def python(parser, args):
|
def python(parser, args, unknown_args):
|
||||||
|
if args.module:
|
||||||
|
sys.argv = ['spack-python'] + unknown_args + args.python_args
|
||||||
|
runpy.run_module(args.module, run_name="__main__", alter_sys=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if unknown_args:
|
||||||
|
tty.die("Unknown arguments:", " ".join(unknown_args))
|
||||||
|
|
||||||
# Fake a main python shell by setting __name__ to __main__.
|
# Fake a main python shell by setting __name__ to __main__.
|
||||||
console = code.InteractiveConsole({'__name__': '__main__',
|
console = code.InteractiveConsole({'__name__': '__main__',
|
||||||
'spack': spack})
|
'spack': spack})
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
from spack.main import SpackCommand
|
from spack.main import SpackCommand
|
||||||
|
|
||||||
@@ -12,3 +14,17 @@
|
|||||||
def test_python():
|
def test_python():
|
||||||
out = python('-c', 'import spack; print(spack.spack_version)')
|
out = python('-c', 'import spack; print(spack.spack_version)')
|
||||||
assert out.strip() == spack.spack_version
|
assert out.strip() == spack.spack_version
|
||||||
|
|
||||||
|
|
||||||
|
def test_python_with_module():
|
||||||
|
# pytest rewrites a lot of modules, which interferes with runpy, so
|
||||||
|
# it's hard to test this. Trying to import a module like sys, that
|
||||||
|
# has no code associated with it, raises an error reliably in python
|
||||||
|
# 2 and 3, which indicates we successfully ran runpy.run_module.
|
||||||
|
with pytest.raises(ImportError, match="No code object"):
|
||||||
|
python('-m', 'sys')
|
||||||
|
|
||||||
|
|
||||||
|
def test_python_raises():
|
||||||
|
out = python('--foobar', fail_on_error=False)
|
||||||
|
assert "Error: Unknown arguments" in out
|
||||||
|
|||||||
@@ -1272,7 +1272,7 @@ _spack_pydoc() {
|
|||||||
_spack_python() {
|
_spack_python() {
|
||||||
if $list_options
|
if $list_options
|
||||||
then
|
then
|
||||||
SPACK_COMPREPLY="-h --help -c"
|
SPACK_COMPREPLY="-h --help -c -m"
|
||||||
else
|
else
|
||||||
SPACK_COMPREPLY=""
|
SPACK_COMPREPLY=""
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user