From 5c0d44fe5f51195a038b270af5f326b378d42e4d Mon Sep 17 00:00:00 2001 From: Ian Bicking Date: Fri, 20 Nov 2009 01:25:32 -0600 Subject: [PATCH] Factor out the base option parser, and the file locations --- pip/__init__.py | 245 +--------------------------------------------- pip/baseparser.py | 222 +++++++++++++++++++++++++++++++++++++++++ pip/locations.py | 32 ++++++ 3 files changed, 257 insertions(+), 242 deletions(-) create mode 100644 pip/baseparser.py create mode 100644 pip/locations.py diff --git a/pip/__init__.py b/pip/__init__.py index b2c97a88c..2f6e3ac59 100755 --- a/pip/__init__.py +++ b/pip/__init__.py @@ -35,9 +35,11 @@ import httplib import time import ConfigParser from distutils.util import strtobool -from distutils import sysconfig from pip.log import logger from pip.backwardcompat import any +from pip.baseparser import parser, ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.locations import build_prefix, src_prefix, site_packages +from pip.locations import user_dir, bin_py class InstallationError(Exception): """General exception during installation""" @@ -51,133 +53,6 @@ class DistributionNotFound(InstallationError): class BadCommand(Exception): """Raised when virtualenv or a command is not found""" -if getattr(sys, 'real_prefix', None): - ## FIXME: is build/ a good name? - build_prefix = os.path.join(sys.prefix, 'build') - src_prefix = os.path.join(sys.prefix, 'src') -else: - ## FIXME: this isn't a very good default - build_prefix = os.path.join(os.getcwd(), 'build') - src_prefix = os.path.join(os.getcwd(), 'src') - -# FIXME doesn't account for venv linked to global site-packages - -site_packages = sysconfig.get_python_lib() -user_dir = os.path.expanduser('~') -if sys.platform == 'win32': - bin_py = os.path.join(sys.prefix, 'Scripts') - # buildout uses 'bin' on Windows too? - if not os.path.exists(bin_py): - bin_py = os.path.join(sys.prefix, 'bin') - config_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming - default_config_file = os.path.join(config_dir, 'pip', 'pip.ini') -else: - bin_py = os.path.join(sys.prefix, 'bin') - default_config_file = os.path.join(user_dir, '.pip', 'pip.conf') - # Forcing to use /usr/local/bin for standard Mac OS X framework installs - if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': - bin_py = '/usr/local/bin' - -class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): - """Custom help formatter for use in ConfigOptionParser that updates - the defaults before expanding them, allowing them to show up correctly - in the help listing""" - - def expand_default(self, option): - if self.parser is not None: - self.parser.update_defaults(self.parser.defaults) - return optparse.IndentedHelpFormatter.expand_default(self, option) - - -class ConfigOptionParser(optparse.OptionParser): - """Custom option parser which updates its defaults by by checking the - configuration files and environmental variables""" - - def __init__(self, *args, **kwargs): - self.config = ConfigParser.RawConfigParser() - self.name = kwargs.pop('name') - self.files = self.get_config_files() - self.config.read(self.files) - assert self.name - optparse.OptionParser.__init__(self, *args, **kwargs) - - def get_config_files(self): - config_file = os.environ.get('PIP_CONFIG_FILE', False) - if config_file and os.path.exists(config_file): - return [config_file] - return [default_config_file] - - def update_defaults(self, defaults): - """Updates the given defaults with values from the config files and - the environ. Does a little special handling for certain types of - options (lists).""" - # Then go and look for the other sources of configuration: - config = {} - # 1. config files - for section in ('global', self.name): - config.update(dict(self.get_config_section(section))) - # 2. environmental variables - config.update(dict(self.get_environ_vars())) - # Then set the options with those values - for key, val in config.iteritems(): - key = key.replace('_', '-') - if not key.startswith('--'): - key = '--%s' % key # only prefer long opts - option = self.get_option(key) - if option is not None: - # ignore empty values - if not val: - continue - # handle multiline configs - if option.action == 'append': - val = val.split() - else: - option.nargs = 1 - if option.action in ('store_true', 'store_false', 'count'): - val = strtobool(val) - try: - val = option.convert_value(key, val) - except optparse.OptionValueError, e: - print ("An error occured during configuration: %s" % e) - sys.exit(3) - defaults[option.dest] = val - return defaults - - def get_config_section(self, name): - """Get a section of a configuration""" - if self.config.has_section(name): - return self.config.items(name) - return [] - - def get_environ_vars(self, prefix='PIP_'): - """Returns a generator with all environmental vars with prefix PIP_""" - for key, val in os.environ.iteritems(): - if key.startswith(prefix): - yield (key.replace(prefix, '').lower(), val) - - def get_default_values(self): - """Overridding to make updating the defaults after instantiation of - the option parser possible, update_defaults() does the dirty work.""" - if not self.process_default_values: - # Old, pre-Optik 1.5 behaviour. - return optparse.Values(self.defaults) - - defaults = self.update_defaults(self.defaults.copy()) # ours - for option in self._get_all_options(): - default = defaults.get(option.dest) - if isinstance(default, basestring): - opt_str = option.get_opt_string() - defaults[option.dest] = option.check_value(opt_str, default) - return optparse.Values(defaults) - -try: - pip_dist = pkg_resources.get_distribution('pip') - version = '%s from %s (python %s)' % ( - pip_dist, pip_dist.location, sys.version[:3]) -except pkg_resources.DistributionNotFound: - # when running pip.py without installing - version=None - def rmtree_errorhandler(func, path, exc_info): """On Windows, the files in .svn are read-only, so when rmtree() tries to remove them, an exception is thrown. We catch that here, remove the @@ -261,120 +136,6 @@ class VcsSupport(object): vcs = VcsSupport() -parser = ConfigOptionParser( - usage='%prog COMMAND [OPTIONS]', - version=version, - add_help_option=False, - formatter=UpdatingDefaultsHelpFormatter(), - name='global') - -parser.add_option( - '-h', '--help', - dest='help', - action='store_true', - help='Show help') -parser.add_option( - '-E', '--environment', - dest='venv', - metavar='DIR', - help='virtualenv environment to run pip in (either give the ' - 'interpreter or the environment base directory)') -parser.add_option( - '-s', '--enable-site-packages', - dest='site_packages', - action='store_true', - help='Include site-packages in virtualenv if one is to be ' - 'created. Ignored if --environment is not used or ' - 'the virtualenv already exists.') -parser.add_option( - # Defines a default root directory for virtualenvs, relative - # virtualenvs names/paths are considered relative to it. - '--virtualenv-base', - dest='venv_base', - type='str', - default='', - help=optparse.SUPPRESS_HELP) -parser.add_option( - # Run only if inside a virtualenv, bail if not. - '--require-virtualenv', '--require-venv', - dest='require_venv', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) -parser.add_option( - # Use automatically an activated virtualenv instead of installing - # globally. -E will be ignored if used. - '--respect-virtualenv', '--respect-venv', - dest='respect_venv', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) - -parser.add_option( - '-v', '--verbose', - dest='verbose', - action='count', - default=0, - help='Give more output') -parser.add_option( - '-q', '--quiet', - dest='quiet', - action='count', - default=0, - help='Give less output') -parser.add_option( - '--log', - dest='log', - metavar='FILENAME', - help='Log file where a complete (maximum verbosity) record will be kept') -parser.add_option( - # Writes the log levels explicitely to the log' - '--log-explicit-levels', - dest='log_explicit_levels', - action='store_true', - default=False, - help=optparse.SUPPRESS_HELP) -parser.add_option( - # The default log file - '--local-log', '--log-file', - dest='log_file', - metavar='FILENAME', - default='./pip-log.txt', - help=optparse.SUPPRESS_HELP) - -parser.add_option( - '--proxy', - dest='proxy', - type='str', - default='', - help="Specify a proxy in the form user:passwd@proxy.server:port. " - "Note that the user:password@ is optional and required only if you " - "are behind an authenticated proxy. If you provide " - "user@proxy.server:port then you will be prompted for a password.") -parser.add_option( - '--timeout', '--default-timeout', - metavar='SECONDS', - dest='timeout', - type='float', - default=15, - help='Set the socket timeout (default %default seconds)') -parser.add_option( - # The default version control system for editables, e.g. 'svn' - '--default-vcs', - dest='default_vcs', - type='str', - default='', - help=optparse.SUPPRESS_HELP) -parser.add_option( - # A regex to be used to skip requirements - '--skip-requirements-regex', - dest='skip_requirements_regex', - type='str', - default='', - help=optparse.SUPPRESS_HELP) - -parser.disable_interspersed_args() - _commands = {} class Command(object): diff --git a/pip/baseparser.py b/pip/baseparser.py new file mode 100644 index 000000000..961fd8324 --- /dev/null +++ b/pip/baseparser.py @@ -0,0 +1,222 @@ +"""Base option parser setup""" + +import sys +import optparse +import pkg_resources +import ConfigParser +import os +from pip.locations import default_config_file + +class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): + """Custom help formatter for use in ConfigOptionParser that updates + the defaults before expanding them, allowing them to show up correctly + in the help listing""" + + def expand_default(self, option): + if self.parser is not None: + self.parser.update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class ConfigOptionParser(optparse.OptionParser): + """Custom option parser which updates its defaults by by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.config = ConfigParser.RawConfigParser() + self.name = kwargs.pop('name') + self.files = self.get_config_files() + self.config.read(self.files) + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def get_config_files(self): + config_file = os.environ.get('PIP_CONFIG_FILE', False) + if config_file and os.path.exists(config_file): + return [config_file] + return [default_config_file] + + def update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + # Then go and look for the other sources of configuration: + config = {} + # 1. config files + for section in ('global', self.name): + config.update(dict(self.get_config_section(section))) + # 2. environmental variables + config.update(dict(self.get_environ_vars())) + # Then set the options with those values + for key, val in config.iteritems(): + key = key.replace('_', '-') + if not key.startswith('--'): + key = '--%s' % key # only prefer long opts + option = self.get_option(key) + if option is not None: + # ignore empty values + if not val: + continue + # handle multiline configs + if option.action == 'append': + val = val.split() + else: + option.nargs = 1 + if option.action in ('store_true', 'store_false', 'count'): + val = strtobool(val) + try: + val = option.convert_value(key, val) + except optparse.OptionValueError, e: + print ("An error occured during configuration: %s" % e) + sys.exit(3) + defaults[option.dest] = val + return defaults + + def get_config_section(self, name): + """Get a section of a configuration""" + if self.config.has_section(name): + return self.config.items(name) + return [] + + def get_environ_vars(self, prefix='PIP_'): + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.iteritems(): + if key.startswith(prefix): + yield (key.replace(prefix, '').lower(), val) + + def get_default_values(self): + """Overridding to make updating the defaults after instantiation of + the option parser possible, update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + defaults = self.update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, basestring): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + +try: + pip_dist = pkg_resources.get_distribution('pip') + version = '%s from %s (python %s)' % ( + pip_dist, pip_dist.location, sys.version[:3]) +except pkg_resources.DistributionNotFound: + # when running pip.py without installing + version=None + +parser = ConfigOptionParser( + usage='%prog COMMAND [OPTIONS]', + version=version, + add_help_option=False, + formatter=UpdatingDefaultsHelpFormatter(), + name='global') + +parser.add_option( + '-h', '--help', + dest='help', + action='store_true', + help='Show help') +parser.add_option( + '-E', '--environment', + dest='venv', + metavar='DIR', + help='virtualenv environment to run pip in (either give the ' + 'interpreter or the environment base directory)') +parser.add_option( + '-s', '--enable-site-packages', + dest='site_packages', + action='store_true', + help='Include site-packages in virtualenv if one is to be ' + 'created. Ignored if --environment is not used or ' + 'the virtualenv already exists.') +parser.add_option( + # Defines a default root directory for virtualenvs, relative + # virtualenvs names/paths are considered relative to it. + '--virtualenv-base', + dest='venv_base', + type='str', + default='', + help=optparse.SUPPRESS_HELP) +parser.add_option( + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) +parser.add_option( + # Use automatically an activated virtualenv instead of installing + # globally. -E will be ignored if used. + '--respect-virtualenv', '--respect-venv', + dest='respect_venv', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) + +parser.add_option( + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output') +parser.add_option( + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help='Give less output') +parser.add_option( + '--log', + dest='log', + metavar='FILENAME', + help='Log file where a complete (maximum verbosity) record will be kept') +parser.add_option( + # Writes the log levels explicitely to the log' + '--log-explicit-levels', + dest='log_explicit_levels', + action='store_true', + default=False, + help=optparse.SUPPRESS_HELP) +parser.add_option( + # The default log file + '--local-log', '--log-file', + dest='log_file', + metavar='FILENAME', + default='./pip-log.txt', + help=optparse.SUPPRESS_HELP) + +parser.add_option( + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form user:passwd@proxy.server:port. " + "Note that the user:password@ is optional and required only if you " + "are behind an authenticated proxy. If you provide " + "user@proxy.server:port then you will be prompted for a password.") +parser.add_option( + '--timeout', '--default-timeout', + metavar='SECONDS', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds)') +parser.add_option( + # The default version control system for editables, e.g. 'svn' + '--default-vcs', + dest='default_vcs', + type='str', + default='', + help=optparse.SUPPRESS_HELP) +parser.add_option( + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=optparse.SUPPRESS_HELP) + +parser.disable_interspersed_args() diff --git a/pip/locations.py b/pip/locations.py new file mode 100644 index 000000000..bd70d9216 --- /dev/null +++ b/pip/locations.py @@ -0,0 +1,32 @@ +"""Locations where we look for configs, install stuff, etc""" + +import sys +import os +from distutils import sysconfig + +if getattr(sys, 'real_prefix', None): + ## FIXME: is build/ a good name? + build_prefix = os.path.join(sys.prefix, 'build') + src_prefix = os.path.join(sys.prefix, 'src') +else: + ## FIXME: this isn't a very good default + build_prefix = os.path.join(os.getcwd(), 'build') + src_prefix = os.path.join(os.getcwd(), 'src') + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_python_lib() +user_dir = os.path.expanduser('~') +if sys.platform == 'win32': + bin_py = os.path.join(sys.prefix, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + config_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming + default_config_file = os.path.join(config_dir, 'pip', 'pip.ini') +else: + bin_py = os.path.join(sys.prefix, 'bin') + default_config_file = os.path.join(user_dir, '.pip', 'pip.conf') + # Forcing to use /usr/local/bin for standard Mac OS X framework installs + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' -- GitLab