Skip to content

Commit

Permalink
Merge pull request #1202 from qwcode/issue-70
Browse files Browse the repository at this point in the history
option parsing fixes
  • Loading branch information
jezdez committed Sep 17, 2013
2 parents 342ea2a + e272d90 commit 1229cf0
Show file tree
Hide file tree
Showing 11 changed files with 370 additions and 254 deletions.
7 changes: 3 additions & 4 deletions docs/pipext.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from docutils.statemachine import ViewList
from textwrap import dedent
from pip import commands
from pip.baseparser import create_main_parser, standard_options
from pip import cmdoptions
from pip.util import get_prog

Expand Down Expand Up @@ -79,19 +78,19 @@ def run(self):

class PipGeneralOptions(PipOptions):
def process_options(self):
self._format_options(standard_options)
self._format_options([o.make() for o in cmdoptions.general_group['options']])


class PipIndexOptions(PipOptions):
def process_options(self):
self._format_options(cmdoptions.index_group['options'])
self._format_options([o.make() for o in cmdoptions.index_group['options']])


class PipCommandOptions(PipOptions):
required_arguments = 1

def process_options(self):
cmd = commands[self.arguments[0]](create_main_parser())
cmd = commands[self.arguments[0]]()
self._format_options(cmd.parser.option_groups[0].option_list, cmd_name=cmd.name)


Expand Down
79 changes: 55 additions & 24 deletions pip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import sys
import re

from pip import cmdoptions
from pip.exceptions import InstallationError, CommandError, PipError
from pip.log import logger
from pip.util import get_installed_distributions, get_prog
from pip.vcs import git, mercurial, subversion, bazaar # noqa
from pip.baseparser import create_main_parser
from pip.commands import commands, get_similar_commands, get_summaries

from pip.baseparser import (ConfigOptionParser, UpdatingDefaultsHelpFormatter,
version)
from pip.commands import commands, get_summaries, get_similar_commands

# The version as used in the setup.py and the docs conf.py
__version__ = "1.5.dev1"
Expand Down Expand Up @@ -60,7 +61,7 @@ def autocomplete():
print(dist)
sys.exit(1)

subcommand = commands[subcommand_name](parser)
subcommand = commands[subcommand_name]()
options += [(opt.get_opt_string(), opt.nargs)
for opt in subcommand.parser.option_list_all
if opt.help != optparse.SUPPRESS_HELP]
Expand Down Expand Up @@ -90,45 +91,75 @@ def autocomplete():
sys.exit(1)


def parseopts(args):
parser = create_main_parser()
def create_main_parser():
parser_kw = {
'usage': '\n%prog <command> [options]',
'add_help_option': False,
'formatter': UpdatingDefaultsHelpFormatter(),
'name': 'global',
'prog': get_prog(),
}

parser = ConfigOptionParser(**parser_kw)
parser.disable_interspersed_args()

# having a default version action just causes trouble
parser.version = version

# add the general options
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
parser.add_option_group(gen_opts)

parser.main = True # so the help formatter knows

# create command listing
# create command listing for description
command_summaries = get_summaries()

description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries]
parser.description = '\n'.join(description)

options, args = parser.parse_args(args)
return parser


def parseopts(args):
parser = create_main_parser()

if options.version:
# Note: parser calls disable_interspersed_args(), so the result of this call
# is to split the initial args into the general options before the
# subcommand and everything else.
# For example:
# args: ['--timeout=5', 'install', '--user', 'INITools']
# general_options: ['--timeout==5']
# args_else: ['install', '--user', 'INITools']
general_options, args_else = parser.parse_args(args)

# --version
if general_options.version:
sys.stdout.write(parser.version)
sys.stdout.write(os.linesep)
sys.exit()

# pip || pip help || pip --help -> print_help()
if not args or (args[0] == 'help' and len(args) == 1):
# pip || pip help -> print_help()
if not args_else or (args_else[0] == 'help' and len(args_else) == 1):
parser.print_help()
sys.exit()

if not args:
msg = ('You must give a command '
'(use "pip --help" to see a list of commands)')
raise CommandError(msg)
# the subcommand name
cmd_name = args_else[0].lower()

command = args[0].lower()
#all the args without the subcommand
cmd_args = args[:]
cmd_args.remove(args_else[0].lower())

if command not in commands:
guess = get_similar_commands(command)
if cmd_name not in commands:
guess = get_similar_commands(cmd_name)

msg = ['unknown command "%s"' % command]
msg = ['unknown command "%s"' % cmd_name]
if guess:
msg.append('maybe you meant "%s"' % guess)

raise CommandError(' - '.join(msg))

return command, options, args, parser
return cmd_name, cmd_args


def main(initial_args=None):
Expand All @@ -138,15 +169,15 @@ def main(initial_args=None):
autocomplete()

try:
cmd_name, options, args, parser = parseopts(initial_args)
cmd_name, cmd_args = parseopts(initial_args)
except PipError:
e = sys.exc_info()[1]
sys.stderr.write("ERROR: %s" % e)
sys.stderr.write(os.linesep)
sys.exit(1)

command = commands[cmd_name](parser) # see baseparser.Command
return command.main(args[1:], options)
command = commands[cmd_name]()
return command.main(cmd_args)


def bootstrap():
Expand Down
51 changes: 13 additions & 38 deletions pip/basecommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import time
import optparse

from pip import cmdoptions
from pip.log import logger
from pip.download import urlopen
from pip.exceptions import (BadCommand, InstallationError, UninstallationError,
Expand All @@ -31,7 +32,7 @@ class Command(object):
usage = None
hidden = False

def __init__(self, main_parser):
def __init__(self):
parser_kw = {
'usage': self.usage,
'prog': '%s %s' % (get_prog(), self.name),
Expand All @@ -40,53 +41,27 @@ def __init__(self, main_parser):
'name': self.name,
'description': self.__doc__,
}
self.main_parser = main_parser

self.parser = ConfigOptionParser(**parser_kw)

# Commands should add options to this option group
optgroup_name = '%s Options' % self.name.capitalize()
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)

# Re-add all options and option groups.
for group in main_parser.option_groups:
self._copy_option_group(self.parser, group)

# Copies all general options from the main parser.
self._copy_options(self.parser, main_parser.option_list)

def _copy_options(self, parser, options):
"""Populate an option parser or group with options."""
for option in options:
if not option.dest:
continue
parser.add_option(option)

def _copy_option_group(self, parser, group):
"""Copy option group (including options) to another parser."""
new_group = optparse.OptionGroup(parser, group.title)
self._copy_options(new_group, group.option_list)

parser.add_option_group(new_group)

def merge_options(self, initial_options, options):
# Make sure we have all global options carried over
attrs = ['log', 'proxy', 'require_venv',
'log_explicit_levels', 'log_file',
'timeout', 'default_vcs',
'skip_requirements_regex',
'no_input', 'exists_action',
'cert']
for attr in attrs:
setattr(options, attr, getattr(initial_options, attr) or getattr(options, attr))
options.quiet += initial_options.quiet
options.verbose += initial_options.verbose
# Add the general options
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, self.parser)
self.parser.add_option_group(gen_opts)


def setup_logging(self):
pass

def main(self, args, initial_options):
options, args = self.parser.parse_args(args)
self.merge_options(initial_options, options)
def parse_args(self, args):
# factored out for testability
return self.parser.parse_args(args)

def main(self, args):
options, args = self.parse_args(args)

level = 1 # Notify
level += options.verbose
Expand Down
Loading

0 comments on commit 1229cf0

Please sign in to comment.