Use a patched argparse only in Python 2.X (#25376)
Spack is internally using a patched version of `argparse` mainly to backport Python 3 functionality into Python 2. This PR makes it such that for the supported Python 3 versions we use `argparse` from the standard Python library. This PR has been extracted from #25371 where it was needed to be able to use recent versions of `pytest`. * Fixed formatting issues when using a pristine argparse.py * Fix error message for Python 3.X when missing positional arguments * Account for the change of API in Python 3.7 * Layout multi-valued args into columns in error messages * Seamless transition in develop if argparse.pyc is in external * Be more defensive in case we can't remove the file.
This commit is contained in:
parent
f444303ce5
commit
09378f56c0
3 changed files with 49 additions and 3 deletions
23
bin/spack
23
bin/spack
|
@ -28,6 +28,7 @@ exit 1
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
min_python3 = (3, 5)
|
min_python3 = (3, 5)
|
||||||
|
@ -70,6 +71,28 @@ if "ruamel.yaml" in sys.modules:
|
||||||
if "ruamel" in sys.modules:
|
if "ruamel" in sys.modules:
|
||||||
del sys.modules["ruamel"]
|
del sys.modules["ruamel"]
|
||||||
|
|
||||||
|
# The following code is here to avoid failures when updating
|
||||||
|
# the develop version, due to spurious argparse.pyc files remaining
|
||||||
|
# in the libs/spack/external directory, see:
|
||||||
|
# https://github.com/spack/spack/pull/25376
|
||||||
|
# TODO: Remove in v0.18.0 or later
|
||||||
|
try:
|
||||||
|
import argparse
|
||||||
|
except ImportError:
|
||||||
|
argparse_pyc = os.path.join(spack_external_libs, 'argparse.pyc')
|
||||||
|
if not os.path.exists(argparse_pyc):
|
||||||
|
raise
|
||||||
|
try:
|
||||||
|
os.remove(argparse_pyc)
|
||||||
|
import argparse # noqa
|
||||||
|
except Exception:
|
||||||
|
msg = ('The file\n\n\t{0}\n\nis corrupted and cannot be deleted by Spack. '
|
||||||
|
'Either delete it manually or ask some administrator to '
|
||||||
|
'delete it for you.')
|
||||||
|
print(msg.format(argparse_pyc))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
import spack.main # noqa
|
import spack.main # noqa
|
||||||
|
|
||||||
# Once we've set up the system path, run the spack main method
|
# Once we've set up the system path, run the spack main method
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
import llnl.util.tty.colify
|
||||||
import llnl.util.tty.color as color
|
import llnl.util.tty.color as color
|
||||||
from llnl.util.tty.log import log_output
|
from llnl.util.tty.log import log_output
|
||||||
|
|
||||||
|
@ -173,14 +174,16 @@ def _format_actions_usage(self, actions, groups):
|
||||||
usage = super(
|
usage = super(
|
||||||
SpackHelpFormatter, self)._format_actions_usage(actions, groups)
|
SpackHelpFormatter, self)._format_actions_usage(actions, groups)
|
||||||
|
|
||||||
|
# Eliminate any occurrence of two or more consecutive spaces
|
||||||
|
usage = re.sub(r'[ ]{2,}', ' ', usage)
|
||||||
|
|
||||||
# compress single-character flags that are not mutually exclusive
|
# compress single-character flags that are not mutually exclusive
|
||||||
# at the beginning of the usage string
|
# at the beginning of the usage string
|
||||||
chars = ''.join(re.findall(r'\[-(.)\]', usage))
|
chars = ''.join(re.findall(r'\[-(.)\]', usage))
|
||||||
usage = re.sub(r'\[-.\] ?', '', usage)
|
usage = re.sub(r'\[-.\] ?', '', usage)
|
||||||
if chars:
|
if chars:
|
||||||
return '[-%s] %s' % (chars, usage)
|
usage = '[-%s] %s' % (chars, usage)
|
||||||
else:
|
return usage.strip()
|
||||||
return usage
|
|
||||||
|
|
||||||
|
|
||||||
class SpackArgumentParser(argparse.ArgumentParser):
|
class SpackArgumentParser(argparse.ArgumentParser):
|
||||||
|
@ -293,7 +296,18 @@ def add_subcommand_group(title, commands):
|
||||||
def add_subparsers(self, **kwargs):
|
def add_subparsers(self, **kwargs):
|
||||||
"""Ensure that sensible defaults are propagated to subparsers"""
|
"""Ensure that sensible defaults are propagated to subparsers"""
|
||||||
kwargs.setdefault('metavar', 'SUBCOMMAND')
|
kwargs.setdefault('metavar', 'SUBCOMMAND')
|
||||||
|
|
||||||
|
# From Python 3.7 we can require a subparser, earlier versions
|
||||||
|
# of argparse will error because required=True is unknown
|
||||||
|
if sys.version_info[:2] > (3, 6):
|
||||||
|
kwargs.setdefault('required', True)
|
||||||
|
|
||||||
sp = super(SpackArgumentParser, self).add_subparsers(**kwargs)
|
sp = super(SpackArgumentParser, self).add_subparsers(**kwargs)
|
||||||
|
# This monkey patching is needed for Python 3.5 and 3.6, which support
|
||||||
|
# having a required subparser but don't expose the API used above
|
||||||
|
if sys.version_info[:2] == (3, 5) or sys.version_info[:2] == (3, 6):
|
||||||
|
sp.required = True
|
||||||
|
|
||||||
old_add_parser = sp.add_parser
|
old_add_parser = sp.add_parser
|
||||||
|
|
||||||
def add_parser(name, **kwargs):
|
def add_parser(name, **kwargs):
|
||||||
|
@ -336,6 +350,15 @@ def format_help(self, level='short'):
|
||||||
# in subparsers, self.prog is, e.g., 'spack install'
|
# in subparsers, self.prog is, e.g., 'spack install'
|
||||||
return super(SpackArgumentParser, self).format_help()
|
return super(SpackArgumentParser, self).format_help()
|
||||||
|
|
||||||
|
def _check_value(self, action, value):
|
||||||
|
# converted value must be one of the choices (if specified)
|
||||||
|
if action.choices is not None and value not in action.choices:
|
||||||
|
cols = llnl.util.tty.colify.colified(
|
||||||
|
sorted(action.choices), indent=4, tty=True
|
||||||
|
)
|
||||||
|
msg = 'invalid choice: %r choose from:\n%s' % (value, cols)
|
||||||
|
raise argparse.ArgumentError(action, msg)
|
||||||
|
|
||||||
|
|
||||||
def make_argument_parser(**kwargs):
|
def make_argument_parser(**kwargs):
|
||||||
"""Create an basic argument parser without any subcommands added."""
|
"""Create an basic argument parser without any subcommands added."""
|
||||||
|
|
Loading…
Reference in a new issue