From d06c98dc6fcda69daac4534d0c3fb030f9a32a9f Mon Sep 17 00:00:00 2001 From: Hugo Lopes Tavares Date: Wed, 2 Jun 2010 23:25:26 -0300 Subject: [PATCH] merged changes related to PEP 8 from hltbra's fork --- pip/__init__.py | 4 +- pip/basecommand.py | 15 +- pip/baseparser.py | 1 + pip/commands/bundle.py | 2 + pip/commands/completion.py | 6 +- pip/commands/freeze.py | 4 +- pip/commands/help.py | 2 + pip/commands/install.py | 2 + pip/commands/search.py | 6 + pip/commands/uninstall.py | 1 + pip/commands/unzip.py | 2 + pip/commands/zip.py | 4 +- pip/exceptions.py | 4 + pip/index.py | 11 +- pip/log.py | 7 + pip/req.py | 20 ++- pip/runner.py | 2 + pip/util.py | 43 ++++- pip/vcs/__init__.py | 5 +- pip/vcs/bazaar.py | 2 + pip/vcs/git.py | 14 +- pip/vcs/subversion.py | 5 +- pip/venv.py | 1 + setup.py | 3 +- tests/auto_test.py | 33 ++-- tests/path.py | 296 ++++++++++++++++---------------- tests/test_all_pip.py | 8 +- tests/test_basic.py | 65 ++++--- tests/test_bundle.py | 4 +- tests/test_cleanup.py | 10 +- tests/test_completion.py | 4 +- tests/test_config.py | 16 +- tests/test_download.py | 7 +- tests/test_file_scheme_index.py | 1 + tests/test_freeze.py | 30 ++-- tests/test_pip.py | 135 +++++++++------ tests/test_proxy.py | 14 +- tests/test_requirements.py | 6 +- tests/test_search.py | 11 +- tests/test_uninstall.py | 36 ++-- tests/test_upgrade.py | 8 +- 41 files changed, 527 insertions(+), 323 deletions(-) diff --git a/pip/__init__.py b/pip/__init__.py index 158f56915..6828be17a 100755 --- a/pip/__init__.py +++ b/pip/__init__.py @@ -11,6 +11,7 @@ from pip.basecommand import command_dict, load_command, load_all_commands from pip.vcs import vcs, get_src_requirement, import_vcs_support from pip.util import get_installed_distributions + def autocomplete(): """Command and option completion for the main option parser (and options) and its subcommands (and options). @@ -18,7 +19,7 @@ def autocomplete(): Enable by sourcing one of the completion shell scripts (bash or zsh). """ # Don't complete if user hasn't sourced bash_completion file. - if not os.environ.has_key('PIP_AUTO_COMPLETE'): + if 'PIP_AUTO_COMPLETE' not in os.environ: return cwords = os.environ['COMP_WORDS'].split()[1:] cword = int(os.environ['COMP_CWORD']) @@ -75,6 +76,7 @@ def autocomplete(): print ' '.join(filter(lambda x: x.startswith(current), subcommands)) sys.exit(1) + def main(initial_args=None): if initial_args is None: initial_args = sys.argv[1:] diff --git a/pip/basecommand.py b/pip/basecommand.py index c53d2d293..958d79e84 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -18,10 +18,12 @@ __all__ = ['command_dict', 'Command', 'load_all_commands', command_dict = {} + class Command(object): name = None usage = None hidden = False + def __init__(self): assert self.name self.parser = ConfigOptionParser( @@ -48,7 +50,7 @@ class Command(object): def setup_logging(self): pass - + def main(self, complete_args, args, initial_options): options, args = self.parser.parse_args(args) self.merge_options(initial_options, options) @@ -65,7 +67,7 @@ class Command(object): logger.explicit_levels = True self.setup_logging() - + if options.require_venv and not options.venv: # If a venv is required check if it can really be found if not os.environ.get('VIRTUAL_ENV'): @@ -141,6 +143,7 @@ class Command(object): log_fp.close() return exit + ## FIXME: should get moved somewhere else: def setup_proxy_handler(proxystr=''): """Set the proxy handler given the option passed on the command @@ -152,6 +155,7 @@ def setup_proxy_handler(proxystr=''): opener = urllib2.build_opener(proxy_support, urllib2.CacheFTPHandler) urllib2.install_opener(opener) + def get_proxy(proxystr=''): """Get the proxy given the option passed on the command line. If an empty string is passed it looks at the HTTP_PROXY environment @@ -174,6 +178,7 @@ def get_proxy(proxystr=''): else: return None + def format_exc(exc_info=None): if exc_info is None: exc_info = sys.exc_info() @@ -181,6 +186,7 @@ def format_exc(exc_info=None): traceback.print_exception(*exc_info, **dict(file=out)) return out.getvalue() + def open_logfile(filename, mode='a'): """Open the named log file in append mode. @@ -193,13 +199,14 @@ def open_logfile(filename, mode='a'): if not os.path.exists(dirname): os.makedirs(dirname) exists = os.path.exists(filename) - + log_fp = open(filename, mode) if exists: print >> log_fp, '-'*60 print >> log_fp, '%s run on %s' % (sys.argv[0], time.strftime('%c')) return log_fp + def load_command(name): full_name = 'pip.commands.%s' % name if full_name in sys.modules: @@ -209,10 +216,12 @@ def load_command(name): except ImportError: pass + def load_all_commands(): for name in command_names(): load_command(name) + def command_names(): dir = os.path.join(os.path.dirname(__file__), 'commands') names = [] diff --git a/pip/baseparser.py b/pip/baseparser.py index b04a8639c..928c1e5a4 100644 --- a/pip/baseparser.py +++ b/pip/baseparser.py @@ -8,6 +8,7 @@ import os from distutils.util import strtobool from pip.locations import default_config_file, default_log_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 diff --git a/pip/commands/bundle.py b/pip/commands/bundle.py index e506a20a5..fb0f75704 100644 --- a/pip/commands/bundle.py +++ b/pip/commands/bundle.py @@ -4,6 +4,7 @@ from pip.log import logger from pip.exceptions import InstallationError from pip.commands.install import InstallCommand + class BundleCommand(InstallCommand): name = 'bundle' usage = '%prog [OPTIONS] BUNDLE_NAME.pybundle PACKAGE_NAMES...' @@ -28,4 +29,5 @@ class BundleCommand(InstallCommand): requirement_set = super(BundleCommand, self).run(options, args) return requirement_set + BundleCommand() diff --git a/pip/commands/completion.py b/pip/commands/completion.py index 94a10380a..d003b9ae3 100644 --- a/pip/commands/completion.py +++ b/pip/commands/completion.py @@ -19,13 +19,13 @@ function _pip_completion { local words cword read -Ac words read -cn cword - reply=( $( COMP_WORDS="$words[*]" \\ + reply=( $( COMP_WORDS="$words[*]" \\ COMP_CWORD=$(( cword-1 )) \\ PIP_AUTO_COMPLETE=1 $words[1] ) ) } compctl -K _pip_completion pip -""" -} +"""} + class CompletionCommand(Command): name = 'completion' diff --git a/pip/commands/freeze.py b/pip/commands/freeze.py index 2f6b13164..01b5df934 100644 --- a/pip/commands/freeze.py +++ b/pip/commands/freeze.py @@ -7,6 +7,7 @@ from pip.log import logger from pip.basecommand import Command from pip.util import get_installed_distributions + class FreezeCommand(Command): name = 'freeze' usage = '%prog [OPTIONS]' @@ -37,7 +38,7 @@ class FreezeCommand(Command): def setup_logging(self): logger.move_stdout_to_stderr() - + def run(self, options, args): requirement = options.requirement find_links = options.find_links or [] @@ -104,4 +105,5 @@ class FreezeCommand(Command): for installation in sorted(installations.values(), key=lambda x: x.name): f.write(str(installation)) + FreezeCommand() diff --git a/pip/commands/help.py b/pip/commands/help.py index 571cd94ad..b0b366112 100644 --- a/pip/commands/help.py +++ b/pip/commands/help.py @@ -2,6 +2,7 @@ from pip.basecommand import Command, command_dict, load_all_commands from pip.exceptions import InstallationError from pip.baseparser import parser + class HelpCommand(Command): name = 'help' usage = '%prog' @@ -27,4 +28,5 @@ class HelpCommand(Command): continue print ' %s: %s' % (command.name, command.summary) + HelpCommand() diff --git a/pip/commands/install.py b/pip/commands/install.py index f6f326498..695ca3f64 100644 --- a/pip/commands/install.py +++ b/pip/commands/install.py @@ -6,6 +6,7 @@ from pip.locations import build_prefix, src_prefix from pip.basecommand import Command from pip.index import PackageFinder + class InstallCommand(Command): name = 'install' usage = '%prog [OPTIONS] PACKAGE_NAMES...' @@ -180,4 +181,5 @@ class InstallCommand(Command): requirement_set.cleanup_files(bundle=self.bundle) return requirement_set + InstallCommand() diff --git a/pip/commands/search.py b/pip/commands/search.py index 7f7f98500..785da3931 100644 --- a/pip/commands/search.py +++ b/pip/commands/search.py @@ -7,6 +7,7 @@ from pip.util import get_terminal_size from pip.log import logger from distutils.version import StrictVersion, LooseVersion + class SearchCommand(Command): name = 'search' usage = '%prog QUERY' @@ -42,6 +43,7 @@ class SearchCommand(Command): hits = pypi.search({'name': query, 'summary': query}, 'or') return hits + def transform_hits(hits): """ The list from pypi is really a list of versions. We want a list of @@ -69,6 +71,7 @@ def transform_hits(hits): package_list = sorted(packages.values(), lambda x, y: cmp(y['score'], x['score'])) return package_list + def print_results(hits, name_column_width=25, terminal_width=None): installed_packages = [p.project_name for p in pkg_resources.working_set] for hit in hits: @@ -96,6 +99,7 @@ def print_results(hits, name_column_width=25, terminal_width=None): except UnicodeEncodeError: pass + def compare_versions(version1, version2): try: return cmp(StrictVersion(version1), StrictVersion(version2)) @@ -103,7 +107,9 @@ def compare_versions(version1, version2): except ValueError: return cmp(LooseVersion(version1), LooseVersion(version2)) + def highest_version(versions): return reduce((lambda v1, v2: compare_versions(v1, v2) == 1 and v1 or v2), versions) + SearchCommand() diff --git a/pip/commands/uninstall.py b/pip/commands/uninstall.py index 1cfe70341..2ceb3c38a 100644 --- a/pip/commands/uninstall.py +++ b/pip/commands/uninstall.py @@ -2,6 +2,7 @@ from pip.req import InstallRequirement, RequirementSet from pip.req import parse_requirements from pip.basecommand import Command + class UninstallCommand(Command): name = 'uninstall' usage = '%prog [OPTIONS] PACKAGE_NAMES ...' diff --git a/pip/commands/unzip.py b/pip/commands/unzip.py index 15ccef072..f83e18205 100644 --- a/pip/commands/unzip.py +++ b/pip/commands/unzip.py @@ -1,7 +1,9 @@ from pip.commands.zip import ZipCommand + class UnzipCommand(ZipCommand): name = 'unzip' summary = 'Unzip individual packages' + UnzipCommand() diff --git a/pip/commands/zip.py b/pip/commands/zip.py index d2a3a74ac..346fc0519 100644 --- a/pip/commands/zip.py +++ b/pip/commands/zip.py @@ -9,6 +9,7 @@ from pip.log import logger from pip.exceptions import InstallationError from pip.basecommand import Command + class ZipCommand(Command): name = 'zip' usage = '%prog [OPTIONS] PACKAGE_NAMES...' @@ -263,7 +264,7 @@ class ZipCommand(Command): if not os.path.isdir(path) and zipfile.is_zipfile(path): zip = zipfile.ZipFile(path, 'r') try: - zip.read(os.path.join(package,'__init__.py')) + zip.read(os.path.join(package, '__init__.py')) except KeyError: pass else: @@ -341,4 +342,5 @@ class ZipCommand(Command): total += len(filenames) return total + ZipCommand() diff --git a/pip/exceptions.py b/pip/exceptions.py index 1152cdc37..1ad1a616d 100644 --- a/pip/exceptions.py +++ b/pip/exceptions.py @@ -1,13 +1,17 @@ """Exceptions used throughout package""" + class InstallationError(Exception): """General exception during installation""" + class UninstallationError(Exception): """General exception during uninstallation""" + class DistributionNotFound(InstallationError): """Raised when a distribution cannot be found to satisfy a requirement""" + class BadCommand(Exception): """Raised when virtualenv or a command is not found""" diff --git a/pip/index.py b/pip/index.py index 0fc6e2b8f..c4a9978dc 100644 --- a/pip/index.py +++ b/pip/index.py @@ -21,6 +21,7 @@ from pip.backwardcompat import WindowsError __all__ = ['PackageFinder'] + class PackageFinder(object): """This finds packages. @@ -77,7 +78,6 @@ class PackageFinder(object): urls.append(url) return files, urls - def find_requirement(self, req, upgrade): url_name = req.url_name # Only check main index if index URL is given: @@ -89,8 +89,9 @@ class PackageFinder(object): page = self._get_page(main_index_url, req) if page is None: url_name = self._find_url_name(Link(self.index_urls[0]), url_name, req) or req.url_name + def mkurl_pypi_url(url): - loc = posixpath.join(url, url_name) + loc = posixpath.join(url, url_name) # For maximum compatibility with easy_install, ensure the path # ends in a trailing slash. Although this isn't in the spec # (and PyPI can handle it without the slash) some other index @@ -331,6 +332,7 @@ class PageCache(object): for url in urls: self._pages[url] = page + class HTMLPage(object): """Represents one page, along with its URL""" @@ -385,7 +387,7 @@ class HTMLPage(object): cache.set_is_archive(url) return None logger.debug('Getting page %s' % url) - + # Tack index.html onto file:// URLs that point to directories (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url) if scheme == 'file' and os.path.isdir(urllib.url2pathname(path)): @@ -517,6 +519,7 @@ class HTMLPage(object): return self._clean_re.sub( lambda match: '%%%2x' % ord(match.group(0)), url) + class Link(object): def __init__(self, url, comes_from=None): @@ -582,6 +585,7 @@ class Link(object): def show_url(self): return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + def get_requirement_from_url(url): """Get a requirement from the URL, if possible. This looks for #egg in the URL""" @@ -591,6 +595,7 @@ def get_requirement_from_url(url): egg_info = splitext(link.filename)[0] return package_to_requirement(egg_info) + def package_to_requirement(package_name): """Translate a name like Foo-1.2 to Foo==1.3""" match = re.search(r'^(.*?)(-dev|-\d.*)', package_name) diff --git a/pip/log.py b/pip/log.py index dafa99c86..0218ab1ae 100644 --- a/pip/log.py +++ b/pip/log.py @@ -4,6 +4,7 @@ import sys import logging + class Logger(object): """ @@ -30,16 +31,22 @@ class Logger(object): def debug(self, msg, *args, **kw): self.log(self.DEBUG, msg, *args, **kw) + def info(self, msg, *args, **kw): self.log(self.INFO, msg, *args, **kw) + def notify(self, msg, *args, **kw): self.log(self.NOTIFY, msg, *args, **kw) + def warn(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) + def error(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) + def fatal(self, msg, *args, **kw): self.log(self.FATAL, msg, *args, **kw) + def log(self, level, msg, *args, **kw): if args: if kw: diff --git a/pip/req.py b/pip/req.py index de4743b16..711a8e84d 100644 --- a/pip/req.py +++ b/pip/req.py @@ -30,6 +30,7 @@ from pip import call_subprocess from pip.backwardcompat import any, md5 from pip.index import Link + class InstallRequirement(object): def __init__(self, req, comes_from, source_dir=None, editable=False, @@ -695,6 +696,7 @@ execfile(__file__) assert self.source_dir return os.path.join(self.source_dir, 'pip-delete-this-directory.txt') + DELETE_MARKER_MESSAGE = '''\ This file is placed here by pip to indicate the source was put here by pip. @@ -703,6 +705,7 @@ Once this package is successfully installed this source code will be deleted (unless you remove this file). ''' + class RequirementSet(object): def __init__(self, build_dir, src_dir, download_dir, download_cache=None, @@ -1334,8 +1337,10 @@ class RequirementSet(object): name = name.replace(os.path.sep, '/') return name + _scheme_re = re.compile(r'^(http|https|file):', re.I) + def parse_requirements(filename, finder=None, comes_from=None, options=None): skip_match = None skip_regex = options.skip_requirements_regex @@ -1372,16 +1377,19 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None): line = line[len('--find-links'):].strip().lstrip('=') ## FIXME: it would be nice to keep track of the source of ## the find_links: - if finder: finder.find_links.append(line) + if finder: + finder.find_links.append(line) elif line.startswith('-i') or line.startswith('--index-url'): if line.startswith('-i'): line = line[2:].strip() else: line = line[len('--index-url'):].strip().lstrip('=') - if finder: finder.index_urls = [line] + if finder: + finder.index_urls = [line] elif line.startswith('--extra-index-url'): line = line[len('--extra-index-url'):].strip().lstrip('=') - if finder: finder.index_urls.append(line) + if finder: + finder.index_urls.append(line) else: comes_from = '-r %s (line %s)' % (filename, line_number) if line.startswith('-e') or line.startswith('--editable'): @@ -1395,6 +1403,7 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None): req = InstallRequirement.from_line(line, comes_from) yield req + def parse_editable(editable_req, default_vcs=None): """Parses svn+http://blahblah@rev#egg=Foobar into a requirement (Foobar) and a URL""" @@ -1437,6 +1446,7 @@ def parse_editable(editable_req, default_vcs=None): req = match.group(1) return req, url + class UninstallPathSet(object): """A set of file paths to be removed in the uninstallation of a requirement.""" @@ -1498,7 +1508,6 @@ class UninstallPathSet(object): return os.path.join( self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) - def remove(self, auto_confirm=False): """Remove paths in ``self.paths`` with confirmation (unless ``auto_confirm`` is True).""" @@ -1569,7 +1578,7 @@ class UninstallPthEntries(object): # paths outside of site-packages, but all the others use forward # slashes. if sys.platform == 'win32' and not os.path.splitdrive(entry)[0]: - entry = entry.replace('\\','/') + entry = entry.replace('\\', '/') self.entries.add(entry) def remove(self): @@ -1601,6 +1610,7 @@ class UninstallPthEntries(object): fh.close() return True + class FakeFile(object): """Wrap a list of lines in an object with readline() to make ConfigParser happy.""" diff --git a/pip/runner.py b/pip/runner.py index 7f0fa7db8..be830ad9a 100644 --- a/pip/runner.py +++ b/pip/runner.py @@ -1,6 +1,7 @@ import sys import os + def run(): base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ## FIXME: this is kind of crude; if we could create a fake pip @@ -10,6 +11,7 @@ def run(): import pip return pip.main() + if __name__ == '__main__': exit = run() if exit: diff --git a/pip/util.py b/pip/util.py index 72834db69..98209ce04 100644 --- a/pip/util.py +++ b/pip/util.py @@ -23,10 +23,12 @@ __all__ = ['rmtree', 'display_path', 'backup_dir', 'make_path_relative', 'normalize_path', 'get_file_content', 'renames', 'get_terminal_size'] + def rmtree(dir): shutil.rmtree(dir, ignore_errors=True, onerror=rmtree_errorhandler) + 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 @@ -43,6 +45,7 @@ def rmtree_errorhandler(func, path, exc_info): # use the original function to repeat the operation func(path) + def display_path(path): """Gives the display value for a given path, making it relative to cwd if possible.""" @@ -51,6 +54,7 @@ def display_path(path): path = '.' + path[len(os.getcwd()):] return path + def backup_dir(dir, ext='.bak'): """Figure out the name of a directory to back up the given dir to (adding .bak, .bak2, etc)""" @@ -61,6 +65,7 @@ def backup_dir(dir, ext='.bak'): extension = ext + str(n) return dir + extension + def splitext(path): """Like os.path.splitext, but take off .tar too""" base, ext = posixpath.splitext(path) @@ -69,6 +74,7 @@ def splitext(path): base = base[:-4] return base, ext + def find_command(cmd, paths=None, pathext=None): """Searches the PATH for the given command and returns its path""" if paths is None: @@ -95,6 +101,7 @@ def find_command(cmd, paths=None, pathext=None): return cmd_path return None + def ask(message, options): """Ask the message interactively, with the given possible responses""" while 1: @@ -108,14 +115,17 @@ def ask(message, options): else: return response + class _Inf(object): """I am bigger than everything!""" def __cmp__(self, a): if self is a: return 0 return 1 + def __repr__(self): return 'Inf' + Inf = _Inf() del _Inf @@ -134,9 +144,11 @@ def url_to_path(url): path = '/' + path return path + _drive_re = re.compile('^([a-z]):', re.I) _url_drive_re = re.compile('^([a-z])[:|]', re.I) + def path_to_url(path): """ Convert a path to a file: URL. The path will be made absolute. @@ -149,6 +161,7 @@ def path_to_url(path): url = url.lstrip('/') return 'file:///' + url + def path_to_url2(path): """ Convert a path to a file: URL. The path will be made absolute and have @@ -162,11 +175,14 @@ def path_to_url2(path): url = url.lstrip('/') return 'file:///' + drive + url + _normalize_re = re.compile(r'[^a-z]', re.I) + def normalize_name(name): return _normalize_re.sub('-', name.lower()) + def format_size(bytes): if bytes > 1000*1000: return '%.1fMb' % (bytes/1000.0/1000) @@ -177,6 +193,7 @@ def format_size(bytes): else: return '%ibytes' % bytes + def is_url(name): """Returns true if the name looks like a URL""" from pip.vcs import vcs @@ -185,6 +202,7 @@ def is_url(name): scheme = name.split(':', 1)[0].lower() return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + def is_installable_dir(path): """Return True if `path` is a directory containing a setup.py file.""" if not os.path.isdir(path): @@ -194,6 +212,7 @@ def is_installable_dir(path): return True return False + def is_archive_file(name): """Return True if `name` is a considered as an archive file.""" archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar', '.pybundle') @@ -202,11 +221,13 @@ def is_archive_file(name): return True return False + def is_svn_page(html): """Returns true if the page appears to be the index page of an svn repository""" return (re.search(r'[^<]*Revision \d+:', html) and re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + def file_contents(filename): fp = open(filename, 'rb') try: @@ -214,6 +235,7 @@ def file_contents(filename): finally: fp.close() + def split_leading_dir(path): path = str(path) path = path.lstrip('/').lstrip('\\') @@ -225,6 +247,7 @@ def split_leading_dir(path): else: return path, '' + def has_leading_dir(paths): """Returns true if all the paths have the same leading path name (i.e., everything is in one subdirectory in an archive)""" @@ -239,6 +262,7 @@ def has_leading_dir(paths): return False return True + def make_path_relative(path, rel_to): """ Make a filename relative, where the filename path, and it is @@ -267,6 +291,7 @@ def make_path_relative(path, rel_to): return '.' + os.path.sep return os.path.sep.join(full_parts) + def normalize_path(path): """ Convert a path to its canonical, case-normalized, absolute version. @@ -274,9 +299,11 @@ def normalize_path(path): """ return os.path.normcase(os.path.realpath(path)) + _scheme_re = re.compile(r'^(http|https|file):', re.I) _url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + def geturl(urllib2_resp): """ Use instead of urllib.addinfourl.geturl(), which appears to have @@ -293,6 +320,7 @@ def geturl(urllib2_resp): else: return '%s//%s' % (scheme, rest) + def get_file_content(url, comes_from=None): """Gets the content of a file; it may be a filename, file: URL, or http: URL. Returns (location, content)""" @@ -323,6 +351,7 @@ def get_file_content(url, comes_from=None): f.close() return url, content + def renames(old, new): """Like os.renames(), but handles renaming across devices.""" # Implementation borrowed from os.renames(). @@ -339,6 +368,7 @@ def renames(old, new): except OSError: pass + def in_venv(): """ Return True if we're running inside a virtualenv, False otherwise. @@ -346,6 +376,7 @@ def in_venv(): """ return hasattr(sys, 'real_prefix') + def is_local(path): """ Return True if path is within sys.prefix, if we're running in a virtualenv. @@ -357,6 +388,7 @@ def is_local(path): return True return normalize_path(path).startswith(normalize_path(sys.prefix)) + def dist_is_local(dist): """ Return True if given Distribution object is installed locally @@ -367,6 +399,7 @@ def dist_is_local(dist): """ return is_local(dist_location(dist)) + def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python')): """ Return a list of installed Distribution objects. @@ -385,6 +418,7 @@ def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'pyt local_test = lambda d: True return [d for d in pkg_resources.working_set if local_test(d) and d.key not in skip] + def egg_link_path(dist): """ Return the path where we'd expect to find a .egg-link file for @@ -398,6 +432,7 @@ def egg_link_path(dist): """ return os.path.join(site_packages, dist.project_name) + '.egg-link' + def dist_location(dist): """ Get the site-packages location of this distribution. Generally @@ -411,12 +446,15 @@ def dist_location(dist): return egg_link return dist.location + def get_terminal_size(): """Returns a tuple (x, y) representing the width(x) and the height(x) in characters of the terminal window.""" def ioctl_GWINSZ(fd): try: - import fcntl, termios, struct + import fcntl + import termios + import struct cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) except: @@ -434,9 +472,10 @@ def get_terminal_size(): cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) return int(cr[1]), int(cr[0]) + # Insurance against "creative" interpretation of the RFC: # http://bugs.python.org/issue8732 def urlopen(url): if isinstance(url, basestring): - url = urllib2.Request(url, headers={'Accept-encoding':'identity'}) + url = urllib2.Request(url, headers={'Accept-encoding': 'identity'}) return urllib2.urlopen(url) diff --git a/pip/vcs/__init__.py b/pip/vcs/__init__.py index 5a85e2424..40c950841 100644 --- a/pip/vcs/__init__.py +++ b/pip/vcs/__init__.py @@ -10,6 +10,7 @@ from pip.log import logger __all__ = ['vcs', 'get_source_requirement', 'import_vcs_support'] + class VcsSupport(object): _registry = {} schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp'] @@ -75,8 +76,8 @@ class VcsSupport(object): return self.get_backend(vc_type) return None -vcs = VcsSupport() +vcs = VcsSupport() class VersionControl(object): @@ -218,6 +219,7 @@ class VersionControl(object): def get_src_requirement(self, dist, location, find_tags=False): raise NotImplementedError + def get_src_requirement(dist, location, find_tags): version_control = vcs.get_backend_from_location(location) if version_control: @@ -225,6 +227,7 @@ def get_src_requirement(dist, location, find_tags): logger.warn('cannot determine version of editable source in %s (is not SVN checkout, Git clone, Mercurial clone or Bazaar branch)' % location) return dist.as_requirement() + def import_vcs_support(): # Import all the version control support modules: here = os.path.dirname(__file__) diff --git a/pip/vcs/bazaar.py b/pip/vcs/bazaar.py index 0793bd41e..8a0deee69 100644 --- a/pip/vcs/bazaar.py +++ b/pip/vcs/bazaar.py @@ -7,6 +7,7 @@ from pip.log import logger from pip.util import rmtree, display_path from pip.vcs import vcs, VersionControl + class Bazaar(VersionControl): name = 'bzr' dirname = '.bzr' @@ -130,4 +131,5 @@ class Bazaar(VersionControl): full_egg_name = '%s-dev_r%s' % (dist.egg_name(), current_rev) return '%s@%s#egg=%s' % (repo, current_rev, full_egg_name) + vcs.register(Bazaar) diff --git a/pip/vcs/git.py b/pip/vcs/git.py index e5ac0000f..67de10648 100644 --- a/pip/vcs/git.py +++ b/pip/vcs/git.py @@ -9,6 +9,7 @@ from pip.log import logger from urllib import url2pathname from urlparse import urlsplit, urlunsplit + class Git(VersionControl): name = 'git' dirname = '.git' @@ -23,15 +24,15 @@ class Git(VersionControl): # Works around an apparent Git bug # (see http://article.gmane.org/gmane.comp.version-control.git/146500) if url: - scheme,netloc,path,query,fragment = urlsplit(url) + scheme, netloc, path, query, fragment = urlsplit(url) if scheme.endswith('file'): initial_slashes = path[:-len(path.lstrip('/'))] - newpath = initial_slashes + url2pathname(path).replace('\\','/').lstrip('/') + newpath = initial_slashes + url2pathname(path).replace('\\', '/').lstrip('/') url = urlunsplit((scheme, netloc, newpath, query, fragment)) after_plus = scheme.find('+')+1 url = scheme[:after_plus]+ urlunsplit((scheme[after_plus:], netloc, newpath, query, fragment)) - super(Git,self).__init__(url, *args, **kwargs) + super(Git, self).__init__(url, *args, **kwargs) def parse_vcs_bundle_file(self, content): url = rev = None @@ -86,7 +87,7 @@ class Git(VersionControl): if rev in revisions: # if rev is a sha return [rev] - inverse_revisions = dict((v,k) for k, v in revisions.iteritems()) + inverse_revisions = dict((v, k) for k, v in revisions.iteritems()) if rev not in inverse_revisions: # is rev a name or tag? origin_rev = 'origin/%s' % rev if origin_rev in inverse_revisions: @@ -197,9 +198,10 @@ class Git(VersionControl): self.url = self.url.replace('git+', 'git+ssh://') url, rev = super(Git, self).get_url_rev() url = url.replace('ssh://', '') - else: + else: url, rev = super(Git, self).get_url_rev() - return url,rev + return url, rev + vcs.register(Git) diff --git a/pip/vcs/subversion.py b/pip/vcs/subversion.py index 0d9503a18..044120d68 100644 --- a/pip/vcs/subversion.py +++ b/pip/vcs/subversion.py @@ -11,6 +11,7 @@ _svn_rev_re = re.compile('committed-rev="(\d+)"') _svn_url_re = re.compile(r'URL: (.+)') _svn_revision_re = re.compile(r'Revision: (.+)') + class Subversion(VersionControl): name = 'svn' dirname = '.svn' @@ -140,7 +141,7 @@ class Subversion(VersionControl): f.close() if data.startswith('8') or data.startswith('9') or data.startswith('10'): - data = map(str.splitlines,data.split('\n\x0c\n')) + data = map(str.splitlines, data.split('\n\x0c\n')) del data[0][0] # get rid of the '8' dirurl = data[0][3] revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0] @@ -190,7 +191,7 @@ class Subversion(VersionControl): data = f.read() f.close() if data.startswith('8') or data.startswith('9') or data.startswith('10'): - data = map(str.splitlines,data.split('\n\x0c\n')) + data = map(str.splitlines, data.split('\n\x0c\n')) del data[0][0] # get rid of the '8' return data[0][3] elif data.startswith('<?xml'): diff --git a/pip/venv.py b/pip/venv.py index dabea0e5c..708abb05b 100644 --- a/pip/venv.py +++ b/pip/venv.py @@ -6,6 +6,7 @@ import subprocess from pip.exceptions import BadCommand from pip.log import logger + def restart_in_venv(venv, base, site_packages, args): """ Restart this script using the interpreter in the given virtual environment diff --git a/setup.py b/setup.py index 5bd15e973..8b6380f5a 100644 --- a/setup.py +++ b/setup.py @@ -32,5 +32,4 @@ setup(name='pip', license='MIT', packages=['pip', 'pip.commands', 'pip.vcs'], entry_points=dict(console_scripts=['pip=pip:main']), - zip_safe=False - ) + zip_safe=False) diff --git a/tests/auto_test.py b/tests/auto_test.py index a187e4771..cf3c30360 100644 --- a/tests/auto_test.py +++ b/tests/auto_test.py @@ -1,12 +1,14 @@ -import sys, os from subprocess import check_call, PIPE from path import Path -import shutil from tempfile import mkdtemp, gettempdir from test_pip import create_virtualenv +import sys +import os +import shutil exe = sys.platform == 'win32' and '.EXE' or '' + def rmtree(path): # From pathutils by Michael Foord: http://www.voidspace.org.uk/python/pathutils.html def onerror(func, path, exc_info): @@ -32,9 +34,11 @@ def rmtree(path): if Path(path).exists: shutil.rmtree(path, onerror=onerror) + def system(*args): check_call(args, stdout=PIPE, shell=(sys.platform=='win32')) + def call(*args): if not '--distribute' in sys.argv: check_call(args) @@ -43,9 +47,11 @@ def call(*args): env['PIP_TEST_USE_DISTRIBUTE']='1' check_call(args, env=env) + def assert_in_path(exe): system(exe, '--version') + def clean(root): print >> sys.stderr, 'Cleaning ...', for dirpath, dirnames, filenames in os.walk(root): @@ -57,8 +63,10 @@ def clean(root): rmtree(root/'pip.egg-info') rmtree(root/'tests'/'test-scratch') rmtree(root/'tests'/'test-cache') - try: os.unlink(root/'tests'/'packages'/'FSPkg'/'FSPkg.egg-info'/'PKG-INFO') - except: pass + try: + os.unlink(root/'tests'/'packages'/'FSPkg'/'FSPkg.egg-info'/'PKG-INFO') + except: + pass print >> sys.stderr, 'ok' @@ -73,7 +81,7 @@ def main(argv): # Make sure all external tools are set up to be used. print >> sys.stderr, 'Checking for installed prerequisites in PATH:', for tool in 'git', 'hg', 'bzr', 'svn': - print >> sys.stderr, tool,'...', + print >> sys.stderr, tool, '...', assert_in_path(tool) print >> sys.stderr, 'ok' @@ -82,8 +90,8 @@ def main(argv): # # Delete everything that could lead to stale test results # - clean( pip_root ) - + clean(pip_root) + save_dir = os.getcwd() temp_dir = mkdtemp('-pip_auto_test') try: @@ -99,8 +107,7 @@ def main(argv): # Make sure it's first in PATH os.environ['PATH'] = str( - Path.pathsep.join(( abs_bin, os.environ['PATH'] )) - ) + Path.pathsep.join((abs_bin, os.environ['PATH']))) # # Install python module testing prerequisites @@ -108,16 +115,18 @@ def main(argv): pip = abs_bin/'pip'+exe download_cache = '--download-cache=' \ + Path(gettempdir())/'pip-test-download-cache' + def pip_install(*pkg): - print >> sys.stderr, ' pip install',' '.join(pkg), '...', + print >> sys.stderr, ' pip install', ' '.join(pkg), '...', call(pip, 'install', '-q', download_cache, *pkg) print >> sys.stderr, 'ok' + pip_install('virtualenv') pip_install('--no-index', '-f', 'http://pypi.python.org/packages/source/n/nose/', 'nose') pip_install('scripttest>=1.0.4') print >> sys.stderr, 'ok' nosetests = abs_bin/'nosetests'+exe - call( nosetests, '-w', pip_root/'tests', *(x for x in argv[1:] if x != '--distribute') ) + call(nosetests, '-w', pip_root/'tests', *(x for x in argv[1:] if x != '--distribute')) finally: os.chdir(save_dir) @@ -127,4 +136,4 @@ def main(argv): if __name__ == '__main__': - main( sys.argv ) + main(sys.argv) diff --git a/tests/path.py b/tests/path.py index 9ab57ec47..5d6fdfed6 100644 --- a/tests/path.py +++ b/tests/path.py @@ -1,194 +1,194 @@ # -*- coding: utf-8 -*- # Author: Aziz Köksal -import os, shutil, sys +import os +import shutil +import sys _base = os.path.supports_unicode_filenames and unicode or str + class Path(_base): - """ Models a path in an object oriented way. """ - sep = os.sep # File system path separator: '/' or '\'. - pathsep = os.pathsep # Separator in the PATH environment variable. - - string = _base + """ Models a path in an object oriented way. """ - def __new__(cls, *paths): - if len(paths): - return _base.__new__(cls, os.path.join(*paths)) - return _base.__new__(cls) + sep = os.sep # File system path separator: '/' or '\'. + pathsep = os.pathsep # Separator in the PATH environment variable. + string = _base - def __div__(self, path): - """ Joins this path with another path. """ - """ path_obj / 'bc.d' """ - """ path_obj / path_obj2 """ - return Path(self, path) + def __new__(cls, *paths): + if len(paths): + return _base.__new__(cls, os.path.join(*paths)) + return _base.__new__(cls) - def __rdiv__(self, path): - """ Joins this path with another path. """ - """ "/home/a" / path_obj """ - return Path(path, self) + def __div__(self, path): + """ Joins this path with another path. """ + """ path_obj / 'bc.d' """ + """ path_obj / path_obj2 """ + return Path(self, path) - def __idiv__(self, path): - """ Like __div__ but also assigns to the variable. """ - """ path_obj /= 'bc.d' """ - return Path(self, path) + def __rdiv__(self, path): + """ Joins this path with another path. """ + """ "/home/a" / path_obj """ + return Path(path, self) - def __floordiv__(self, paths): - """ Returns a list of paths prefixed with 'self'. """ - """ '/home/a' // [bc.d, ef.g] -> [/home/a/bc.d, /home/a/ef.g] """ - return [Path(self, path) for path in paths] + def __idiv__(self, path): + """ Like __div__ but also assigns to the variable. """ + """ path_obj /= 'bc.d' """ + return Path(self, path) + def __floordiv__(self, paths): + """ Returns a list of paths prefixed with 'self'. """ + """ '/home/a' // [bc.d, ef.g] -> [/home/a/bc.d, /home/a/ef.g] """ + return [Path(self, path) for path in paths] + def __sub__(self, path): + """ Makes this path relative to another path. """ + """ path_obj - '/home/a' """ + """ path_obj - path_obj2 """ + return Path(os.path.relpath(self, path)) - def __sub__(self, path): - """ Makes this path relative to another path. """ - """ path_obj - '/home/a' """ - """ path_obj - path_obj2 """ - return Path(os.path.relpath(self, path)) + def __rsub__(self, path): + """ Returns path relative to this path. """ + """ "/home/a" - path_obj """ + return Path(os.path.relpath(path, self)) - def __rsub__(self, path): - """ Returns path relative to this path. """ - """ "/home/a" - path_obj """ - return Path(os.path.relpath(path, self)) + def __add__(self, path): + """ Path('/home/a') + 'bc.d' -> '/home/abc.d' """ + return Path(_base(self) + path) + def __radd__(self, path): + """ '/home/a' + Path('bc.d') -> '/home/abc.d' """ + return Path(path + _base(self)) - def __add__(self, path): - """ Path('/home/a') + 'bc.d' -> '/home/abc.d' """ - return Path(_base(self) + path) + def __repr__(self): + return u"Path(%s)" % _base.__repr__(self) - def __radd__(self, path): - """ '/home/a' + Path('bc.d') -> '/home/abc.d' """ - return Path(path + _base(self)) + def __hash__(self): + return _base.__hash__(self) - def __repr__(self): - return u"Path(%s)" % _base.__repr__(self) + @property + def name(self): + """ '/home/a/bc.d' -> 'bc.d' """ + return os.path.basename(self) - def __hash__(self): - return _base.__hash__(self) + @property + def namebase(self): + """ '/home/a/bc.d' -> 'bc' """ + return self.noext.name - @property - def name(self): - """ '/home/a/bc.d' -> 'bc.d' """ - return os.path.basename(self) + @property + def noext(self): + """ '/home/a/bc.d' -> '/home/a/bc' """ + return Path(os.path.splitext(self)[0]) - @property - def namebase(self): - """ '/home/a/bc.d' -> 'bc' """ - return self.noext.name + @property + def ext(self): + """ '/home/a/bc.d' -> '.d' """ + return Path(os.path.splitext(self)[1]) - @property - def noext(self): - """ '/home/a/bc.d' -> '/home/a/bc' """ - return Path(os.path.splitext(self)[0]) + @property + def abspath(self): + """ './a/bc.d' -> '/home/a/bc.d' """ + return Path(os.path.abspath(self)) - @property - def ext(self): - """ '/home/a/bc.d' -> '.d' """ - return Path(os.path.splitext(self)[1]) + @property + def realpath(self): + """ Resolves symbolic links. """ + return Path(os.path.realpath(self)) - @property - def abspath(self): - """ './a/bc.d' -> '/home/a/bc.d' """ - return Path(os.path.abspath(self)) + @property + def normpath(self): + """ '/home/x/.././a//bc.d' -> '/home/a/bc.d' """ + return Path(os.path.normpath(self)) - @property - def realpath(self): - """ Resolves symbolic links. """ - return Path(os.path.realpath(self)) + @property + def normcase(self): + """ Deals with case-insensitive filesystems """ + return Path(os.path.normcase(self)) - @property - def normpath(self): - """ '/home/x/.././a//bc.d' -> '/home/a/bc.d' """ - return Path(os.path.normpath(self)) + @property + def folder(self): + """ Returns the folder of this path. """ + """ '/home/a/bc.d' -> '/home/a' """ + """ '/home/a/' -> '/home/a' """ + """ '/home/a' -> '/home' """ + return Path(os.path.dirname(self)) - @property - def normcase(self): - """ Deals with case-insensitive filesystems """ - return Path(os.path.normcase(self)) + @property + def exists(self): + """ Returns True if the path exists. """ + return os.path.exists(self) - @property - def folder(self): - """ Returns the folder of this path. """ - """ '/home/a/bc.d' -> '/home/a' """ - """ '/home/a/' -> '/home/a' """ - """ '/home/a' -> '/home' """ - return Path(os.path.dirname(self)) + @property + def atime(self): + """ Returns last accessed time. """ + return os.path.getatime(self) - @property - def exists(self): - """ Returns True if the path exists. """ - return os.path.exists(self) + @property + def mtime(self): + """ Returns last modified time. """ + return os.path.getmtime(self) - @property - def atime(self): - """ Returns last accessed time. """ - return os.path.getatime(self) + @property + def ctime(self): + """ Returns last changed time. """ + return os.path.getctime(self) - @property - def mtime(self): - """ Returns last modified time. """ - return os.path.getmtime(self) + @classmethod + def supports_unicode(self): + """ Returns True if the system can handle Unicode file names. """ + return os.path.supports_unicode_filenames() - @property - def ctime(self): - """ Returns last changed time. """ - return os.path.getctime(self) + def walk(self, **kwargs): + """ Returns a generator that walks through a directory tree. """ + if "followlinks" in kwargs: + from sys import version_info as vi + if vi[0]*10+vi[1] < 26: # Only Python 2.6 or newer supports followlinks + del kwargs["followlinks"] + return os.walk(self, **kwargs) - @classmethod - def supports_unicode(self): - """ Returns True if the system can handle Unicode file names. """ - return os.path.supports_unicode_filenames() + def mkdir(self, mode=0777): + """ Creates a directory, if it doesn't exist already. """ + if not self.exists: + os.mkdir(self, mode) - def walk(self, **kwargs): - """ Returns a generator that walks through a directory tree. """ - if "followlinks" in kwargs: - from sys import version_info as vi - if vi[0]*10+vi[1] < 26: # Only Python 2.6 or newer supports followlinks. - del kwargs["followlinks"] - return os.walk(self, **kwargs) + def makedirs(self, mode=0777): + """ Like mkdir(), but also creates parent directories. """ + if not self.exists: + os.makedirs(self, mode) - def mkdir(self, mode=0777): - """ Creates a directory, if it doesn't exist already. """ - if not self.exists: - os.mkdir(self, mode) + def remove(self): + """ Removes a file. """ + os.remove(self) + rm = remove # Alias. - def makedirs(self, mode=0777): - """ Like mkdir(), but also creates parent directories. """ - if not self.exists: - os.makedirs(self, mode) + def rmdir(self): + """ Removes a directory. """ + return os.rmdir(self) - def remove(self): - """ Removes a file. """ - os.remove(self) - rm = remove # Alias. + def rmtree(self, noerrors=True): + """ Removes a directory tree. Ignores errors by default. """ + return shutil.rmtree(self, ignore_errors=noerrors) - def rmdir(self): - """ Removes a directory. """ - return os.rmdir(self) + def copy(self, to): + shutil.copy(self, to) - def rmtree(self, noerrors=True): - """ Removes a directory tree. Ignores errors by default. """ - return shutil.rmtree(self, ignore_errors=noerrors) + def copytree(self, to): + """ Copies a directory tree to another path. """ + shutil.copytree(self, to) - def copy(self, to): - shutil.copy(self, to) + def move(self, to): + """ Moves a file or directory to another path. """ + shutil.move(self, to) - def copytree(self, to): - """ Copies a directory tree to another path. """ - shutil.copytree(self, to) + def rename(self, to): + """ Renames a file or directory. May throw an OSError. """ + os.rename(self, to) - def move(self, to): - """ Moves a file or directory to another path. """ - shutil.move(self, to) + def renames(self, to): + os.renames(self, to) - def rename(self, to): - """ Renames a file or directory. May throw an OSError. """ - os.rename(self, to) - - def renames(self, to): - os.renames(self, to) - - def glob(self, pattern): - from glob import glob - return map(Path, glob(_base(self/pattern))) + def glob(self, pattern): + from glob import glob + return map(Path, glob(_base(self/pattern))) curdir = Path(os.path.curdir) diff --git a/tests/test_all_pip.py b/tests/test_all_pip.py index 1090c2456..2d058df07 100644 --- a/tests/test_all_pip.py +++ b/tests/test_all_pip.py @@ -12,6 +12,7 @@ def all_projects(): projects = [m.group(1) for m in re.finditer(r'<a.*?>(.+)</a>', data)] return projects + def main(args=None): if args is None: args = sys.argv[1:] @@ -36,6 +37,7 @@ def main(args=None): _test_packages(output, pending_fn) print 'Finished all pending!' + def _test_packages(output, pending_fn): package = get_last_item(pending_fn) print 'Testing package %s' % package @@ -72,13 +74,15 @@ def _test_packages(output, pending_fn): add_package(os.path.join(output, 'success.txt'), package) pop_last_item(pending_fn, package) shutil.rmtree(dest_dir) - + + def get_last_item(fn): f = open(fn, 'r') lines = f.readlines() f.close() return lines[-1].strip() + def pop_last_item(fn, line=None): f = open(fn, 'r') lines = f.readlines() @@ -90,10 +94,12 @@ def pop_last_item(fn, line=None): f.writelines(lines) f.close() + def add_package(filename, package): f = open(filename, 'a') f.write(package + '\n') f.close() + if __name__ == '__main__': main() diff --git a/tests/test_basic.py b/tests/test_basic.py index 1661503de..6a6228543 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -2,10 +2,11 @@ from os.path import abspath, exists, join, dirname, curdir, pardir from test_pip import here, reset_env, run_pip, pyversion, mkdir from path import Path + def test_correct_pip_version(): """ Check we are running proper version of pip in run_pip. - + """ reset_env() @@ -16,21 +17,23 @@ def test_correct_pip_version(): result = run_pip('--version') # compare the directory tree of the invoked pip with that of this source distribution - import re,filecmp + import re + import filecmp dir = re.match(r'\s*pip\s\S+\sfrom\s+(.*)\s\([^(]+\)$', result.stdout).group(1) - diffs = filecmp.dircmp(join(base,'pip'), join(dir,'pip')) + diffs = filecmp.dircmp(join(base, 'pip'), join(dir, 'pip')) # If any non-matching .py files exist, we have a problem: run_pip # is picking up some other version! N.B. if this project acquires # primary resources other than .py files, this code will need # maintenance mismatch_py = [x for x in diffs.left_only + diffs.right_only + diffs.diff_files if x.endswith('.py')] - assert not mismatch_py, 'mismatched source files in %r and %r'% (join(base,'pip'), join(dir,'pip')) + assert not mismatch_py, 'mismatched source files in %r and %r'% (join(base, 'pip'), join(dir, 'pip')) + def test_distutils_configuration_setting(): """ Test the distutils-configuration-setting command (which is distinct from other commands). - + """ #print run_pip('-vv', '--distutils-cfg=easy_install:index_url:http://download.zope.org/ppix/', expect_error=True) #Script result: python ../../poacheggs.py -E .../poacheggs-tests/test-scratch -vv --distutils-cfg=easy_install:index_url:http://download.zope.org/ppix/ @@ -42,20 +45,22 @@ def test_distutils_configuration_setting(): #-- updated: ------------------- # lib/python2.4/distutils/distutils.cfg (346 bytes) + def test_install_from_pypi(): """ Test installing a package from PyPI. - + """ e = reset_env() result = run_pip('install', '-vvv', 'INITools==0.2') assert (e.site_packages / 'INITools-0.2-py%s.egg-info' % pyversion) in result.files_created, str(result) assert (e.site_packages / 'initools') in result.files_created, str(result) + def test_editable_install(): """ Test editable installation. - + """ reset_env() result = run_pip('install', '-e', 'INITools==0.2', expect_error=True) @@ -63,19 +68,21 @@ def test_editable_install(): assert len(result.files_created) == 1, result.files_created assert not result.files_updated, result.files_updated + def test_install_editable_from_svn(): """ Test checking out from svn. - + """ e = reset_env() result = run_pip('install', '-e', 'svn+http://svn.colorstudy.com/INITools/trunk#egg=initools-dev') result.assert_installed('INITools', with_files=['.svn']) + def test_download_editable_to_custom_path(): """ Test downloading an editable using a relative custom src folder. - + """ reset_env() mkdir('customdl') @@ -84,14 +91,15 @@ def test_download_editable_to_custom_path(): customsrc = Path('scratch')/'customsrc'/'initools' assert customsrc in result.files_created, sorted(result.files_created.keys()) assert customsrc/'setup.py' in result.files_created, sorted(result.files_created.keys()) - + customdl = Path('scratch')/'customdl'/'initools' assert [filename for filename in result.files_created.keys() if filename.startswith(customdl)] + def test_editable_no_install_followed_by_no_download(): """ Test installing an editable in two steps (first with --no-install, then with --no-download). - + """ reset_env() @@ -100,13 +108,14 @@ def test_editable_no_install_followed_by_no_download(): result.assert_installed('INITools', without_egg_link=True, with_files=['.svn']) result = run_pip('install', '-e', 'svn+http://svn.colorstudy.com/INITools/trunk#egg=initools-dev', - '--no-download', expect_error=True) + '--no-download', expect_error=True) result.assert_installed('INITools', without_files=[curdir, '.svn']) + def test_no_install_followed_by_no_download(): """ Test installing in two steps (first with --no-install, then with --no-download). - + """ env = reset_env() @@ -117,62 +126,68 @@ def test_no_install_followed_by_no_download(): assert build_dir in result.files_created, result.files_created assert build_dir/'INITools.egg-info' in result.files_created - result = run_pip('install', 'INITools==0.2', '--no-download', expect_error=True) + result = run_pip('install', 'INITools==0.2', '--no-download', expect_error=True) assert (env.site_packages/'INITools-0.2-py%s.egg-info' % pyversion) in result.files_created, str(result) assert (env.site_packages/'initools') in result.files_created, sorted(result.files_created.keys()) assert build_dir not in result.files_created assert build_dir/'INITools.egg-info' not in result.files_created + def test_bad_install_with_no_download(): """ Test that --no-download behaves sensibly if the package source can't be found. - + """ reset_env() - result = run_pip('install', 'INITools==0.2', '--no-download', expect_error=True) + result = run_pip('install', 'INITools==0.2', '--no-download', expect_error=True) assert result.stdout.find("perhaps --no-download was used without first running an equivalent install with --no-install?") > 0 + def test_install_dev_version_from_pypi(): """ Test using package==dev. - + """ e = reset_env() result = run_pip('install', 'INITools==dev', expect_error=True) assert (e.site_packages / 'initools') in result.files_created, str(result.stdout) + def test_install_editable_from_git(): """ Test cloning from Git. - + """ e = reset_env() result = run_pip('install', '-e', 'git://github.com/jezdez/django-feedutil.git#egg=django-feedutil', expect_error=True) result.assert_installed('django-feedutil', with_files=['.git']) + def test_install_editable_from_hg(): """ Test cloning from Mercurial. - + """ e = reset_env() result = run_pip('install', '-e', 'hg+http://bitbucket.org/ubernostrum/django-registration/#egg=django-registration', expect_error=True) result.assert_installed('django-registration', with_files=['.hg']) + def test_vcs_url_final_slash_normalization(): """ Test that presence or absence of final slash in VCS URL is normalized. - """ + reset_env() result = run_pip('install', '-e', 'hg+http://bitbucket.org/ubernostrum/django-registration#egg=django-registration', expect_error=True) assert 'pip-log.txt' not in result.files_created, result.files_created['pip-log.txt'].bytes + def test_install_editable_from_bazaar(): """ Test checking out from Bazaar. - + """ e = reset_env() result = run_pip('install', '-e', 'bzr+http://bazaar.launchpad.net/%7Edjango-wikiapp/django-wikiapp/release-0.1/@174#egg=django-wikiapp', expect_error=True) @@ -182,12 +197,13 @@ def test_install_editable_from_bazaar(): def test_vcs_url_urlquote_normalization(): """ Test that urlquoted characters are normalized for repo URL comparison. - + """ reset_env() result = run_pip('install', '-e', 'bzr+http://bazaar.launchpad.net/~django-wikiapp/django-wikiapp/release-0.1#egg=django-wikiapp', expect_error=True) assert 'pip-log.txt' not in result.files_created, result.files_created['pip-log.txt'].bytes + def test_install_from_local_directory(): """ Test installing from a local directory. @@ -199,17 +215,19 @@ def test_install_from_local_directory(): assert (env.site_packages/'fspkg') in result.files_created, str(result.stdout) assert (env.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion) in result.files_created, str(result) + def test_install_from_local_directory_with_no_setup_py(): """ Test installing from a local directory with no 'setup.py'. - """ + reset_env() result = run_pip('install', here, expect_error=True) assert len(result.files_created) == 1, result.files_created assert 'pip-log.txt' in result.files_created, result.files_created assert "is not installable. File 'setup.py' not found." in result.stdout + def test_install_curdir(): """ Test installing current directory ('.'). @@ -221,6 +239,7 @@ def test_install_curdir(): assert (env.site_packages/'fspkg') in result.files_created, str(result.stdout) assert (env.site_packages/'FSPkg-0.1dev-py%s.egg-info' % pyversion) in result.files_created, str(result) + def test_install_pardir(): """ Test installing parent directory ('..'). diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 77cb5f5f6..8b6052af6 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -1,11 +1,11 @@ - import zipfile import textwrap from os.path import abspath, join, dirname, pardir -from test_pip import here, reset_env, run_pip, write_file +from test_pip import here, reset_env, run_pip, write_file from path import Path from pip.util import path_to_url2 + def test_create_bundle(): """ Test making a bundle. We'll grab one package from the filesystem diff --git a/tests/test_cleanup.py b/tests/test_cleanup.py index 6ae3fbca6..20158884d 100644 --- a/tests/test_cleanup.py +++ b/tests/test_cleanup.py @@ -1,15 +1,14 @@ - import zipfile import textwrap from os.path import abspath, exists, join from test_pip import here, reset_env, run_pip, write_file -from path import Path; +from path import Path def test_cleanup_after_install_from_pypi(): """ Test clean up after installing a package from PyPI. - + """ env = reset_env() result = run_pip('install', 'INITools==0.2', expect_error=True) @@ -18,10 +17,11 @@ def test_cleanup_after_install_from_pypi(): assert not exists(build), "build/ dir still exists: %s" % build assert not exists(src), "unexpected src/ dir exists: %s" % src + def test_cleanup_after_install_editable_from_hg(): """ Test clean up after cloning from Mercurial. - + """ env = reset_env() result = run_pip('install', '-e', 'hg+http://bitbucket.org/ubernostrum/django-registration/#egg=django-registration', expect_error=True) @@ -30,6 +30,7 @@ def test_cleanup_after_install_editable_from_hg(): assert not exists(build), "build/ dir still exists: %s" % build assert exists(src), "expected src/ dir doesn't exist: %s" % src + def test_cleanup_after_install_from_local_directory(): """ Test clean up after installing from a local directory. @@ -43,6 +44,7 @@ def test_cleanup_after_install_from_local_directory(): assert not exists(build), "unexpected build/ dir exists: %s" % build assert not exists(src), "unexpected src/ dir exist: %s" % src + def test_cleanup_after_create_bundle(): """ Test clean up after making a bundle. Make sure (build|src)-bundle/ dirs are removed but not src/. diff --git a/tests/test_completion.py b/tests/test_completion.py index e2d0625e5..d28d287dc 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -20,7 +20,6 @@ complete -o default -F _pip_completion pip""" assert bash_completion in result.stdout, 'bash completion is wrong' - def test_completion_for_zsh(): """ Test getting completion for zsh shell @@ -31,7 +30,7 @@ function _pip_completion { local words cword read -Ac words read -cn cword - reply=( $( COMP_WORDS="$words[*]" \\ + reply=( $( COMP_WORDS="$words[*]" \\ COMP_CWORD=$(( cword-1 )) \\ PIP_AUTO_COMPLETE=1 $words[1] ) ) } @@ -41,7 +40,6 @@ compctl -K _pip_completion pip""" assert zsh_completion in result.stdout, 'zsh completion is wrong' - def test_completion_for_unknown_shell(): """ Test getting completion for an unknown shell diff --git a/tests/test_config.py b/tests/test_config.py index ecddedf3a..bc87e05ee 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,13 +1,13 @@ - import tempfile import textwrap from test_pip import here, reset_env, run_pip, clear_environ, write_file import os + def test_options_from_env_vars(): """ Test if ConfigOptionParser reads env vars (e.g. not using PyPI here) - + """ environ = clear_environ(os.environ.copy()) environ['PIP_NO_INDEX'] = '1' @@ -16,10 +16,11 @@ def test_options_from_env_vars(): assert "Ignoring indexes:" in result.stdout, str(result) assert "DistributionNotFound: No distributions at all found for INITools" in result.stdout + def test_command_line_options_override_env_vars(): """ Test that command line options override environmental variables. - + """ environ = clear_environ(os.environ.copy()) environ['PIP_INDEX_URL'] = 'http://pypi.appspot.com/' @@ -31,10 +32,11 @@ def test_command_line_options_override_env_vars(): assert "http://pypi.appspot.com/INITools" not in result.stdout assert "Getting page http://download.zope.org/ppix" in result.stdout + def test_command_line_append_flags(): """ Test command line flags that append to defaults set by environmental variables. - + """ environ = clear_environ(os.environ.copy()) environ['PIP_FIND_LINKS'] = 'http://pypi.pinaxproject.com' @@ -46,11 +48,12 @@ def test_command_line_append_flags(): assert "Analyzing links from page http://pypi.pinaxproject.com" in result.stdout assert "Analyzing links from page http://example.com" in result.stdout + def test_config_file_override_stack(): """ Test config files (global, overriding a global config with a local, overriding all with a command line flag). - + """ f, config_file = tempfile.mkstemp('-pip.cfg', 'test-') environ = clear_environ(os.environ.copy()) @@ -76,10 +79,11 @@ def test_config_file_override_stack(): assert "Getting page http://pypi.appspot.com/INITools" not in result.stdout assert "Getting page http://pypi.python.org/simple/INITools" in result.stdout + def test_log_file_no_directory(): """ Test opening a log file with no directory name. - + """ from pip.basecommand import open_logfile fp = open_logfile('testpip.log') diff --git a/tests/test_download.py b/tests/test_download.py index 661b82da1..5adf9c297 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -1,23 +1,26 @@ from os import makedirs from os.path import join import textwrap -from test_pip import here, reset_env, run_pip, pyversion, write_file +from test_pip import here, reset_env, run_pip, pyversion, write_file from path import Path + def test_download_if_requested(): """ It should download (in the scratch path) and not install if requested. - """ + env = reset_env() result = run_pip('install', 'INITools==0.1', '-d', '.', expect_error=True) assert Path('scratch')/ 'INITools-0.1.tar.gz' in result.files_created assert env.site_packages/ 'initools' not in result.files_created + def test_single_download_from_requirements_file(): """ It should support download (in the scratch path) from PyPi from a requirements file """ + env = reset_env() write_file('test-req.txt', textwrap.dedent(""" INITools==0.1 diff --git a/tests/test_file_scheme_index.py b/tests/test_file_scheme_index.py index c5cecae3d..f7f8f8c3e 100644 --- a/tests/test_file_scheme_index.py +++ b/tests/test_file_scheme_index.py @@ -5,6 +5,7 @@ from path import Path index_url = 'file://' + urllib2.quote(str(Path(here).abspath/'in dex').replace('\\', '/')) + def test_install(): """ Test installing from a local index. diff --git a/tests/test_freeze.py b/tests/test_freeze.py index f725754d4..955c7459d 100644 --- a/tests/test_freeze.py +++ b/tests/test_freeze.py @@ -1,11 +1,13 @@ - -import os, sys, re +import os +import sys +import re import textwrap from doctest import OutputChecker, ELLIPSIS -from test_pip import reset_env, run_pip, pyversion, write_file, get_env +from test_pip import reset_env, run_pip, pyversion, write_file, get_env distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE) + def _check_output(result, expected): checker = OutputChecker() actual = str(result) @@ -19,15 +21,17 @@ def _check_output(result, expected): ## thing to do in the end is probably to find out how to report ## the proper fully-cased package name in our error message. if sys.platform == 'win32': - actual = actual.replace('initools','INITools') + actual = actual.replace('initools', 'INITools') # This allows our existing tests to work when run in a context # with distribute installed. actual = distribute_re.sub('', actual) - def banner(msg): return '\n========== %s ==========\n'%msg + def banner(msg): + return '\n========== %s ==========\n' % msg assert checker.check_output(expected, actual, ELLIPSIS), banner('EXPECTED')+expected+banner('ACTUAL')+actual+banner(6*'=') + def test_freeze(): """ Some tests of freeze, first we have to install some stuff. Note that @@ -38,7 +42,7 @@ def test_freeze(): TODO: refactor this test into multiple tests? (and maybe different test style instead of using doctest output checker) - + """ env = reset_env() write_file('initools-req.txt', textwrap.dedent("""\ @@ -96,15 +100,16 @@ def test_freeze(): <BLANKLINE>""") _check_output(result, expected) + def test_freeze_git_clone(): """ Test freezing a Git clone. - + """ env = reset_env() result = env.run('git', 'clone', 'git://github.com/jezdez/django-pagination.git', 'django-pagination') result = env.run('git', 'checkout', '1df6507872d73ee387eb375428eafbfc253dfcd8', - cwd= env.scratch_path/ 'django-pagination', expect_stderr=True) + cwd=env.scratch_path/'django-pagination', expect_stderr=True) result = env.run('python', 'setup.py', 'develop', cwd=env.scratch_path / 'django-pagination') result = run_pip('freeze', expect_stderr=True) @@ -124,10 +129,11 @@ def test_freeze_git_clone(): ...""") _check_output(result, expected) + def test_freeze_mercurial_clone(): """ Test freezing a Mercurial clone. - + """ reset_env() env = get_env() @@ -151,10 +157,11 @@ def test_freeze_mercurial_clone(): ...""") _check_output(result, expected) + def test_freeze_bazaar_clone(): """ Test freezing a Bazaar clone. - + """ reset_env() env = get_env() @@ -178,10 +185,11 @@ def test_freeze_bazaar_clone(): ...""") _check_output(result, expected) + def test_freeze_with_local_option(): """ Test that wsgiref (from global site-packages) is reported normally, but not with --local. - + """ reset_env() result = run_pip('install', 'initools==0.2') diff --git a/tests/test_pip.py b/tests/test_pip.py index ff990709d..c2e43d220 100644 --- a/tests/test_pip.py +++ b/tests/test_pip.py @@ -1,5 +1,11 @@ #!/usr/bin/env python -import os, sys, tempfile, shutil, glob, atexit, textwrap +import os +import sys +import tempfile +import shutil +import glob +import atexit +import textwrap from path import * from scripttest import TestFileEnvironment @@ -10,32 +16,35 @@ pyversion = sys.version[:3] here = Path(__file__).abspath.folder # the root of this pip source distribution -src = os.path.dirname(here) +src = os.path.dirname(here) download_cache = os.path.join(tempfile.mkdtemp(), 'pip-test-cache') + def demand_dirs(path): - if not os.path.exists(path): + if not os.path.exists(path): os.makedirs(path) - + demand_dirs(download_cache) # Tweak the path so we can find up-to-date pip sources # (http://bitbucket.org/ianb/pip/issue/98) sys.path = [src] + sys.path + def create_virtualenv(where, distribute=False): save_argv = sys.argv - + try: import virtualenv distribute_opt = distribute and ['--distribute'] or [] sys.argv = ['virtualenv', '--quiet'] + distribute_opt + ['--no-site-packages', '--unzip-setuptools', where] virtualenv.main() - finally: + finally: sys.argv = save_argv return virtualenv.path_locations(where) + def relpath(root, other): """a poor man's os.path.relpath, since we may not have Python 2.6""" prefix = root+Path.sep @@ -45,6 +54,7 @@ def relpath(root, other): if 'PYTHONPATH' in os.environ: del os.environ['PYTHONPATH'] + try: any except NameError: @@ -54,16 +64,18 @@ except NameError: return True return False + def clear_environ(environ): return dict(((k, v) for k, v in environ.iteritems() if not k.lower().startswith('pip_'))) + def install_setuptools(env): easy_install = os.path.join(env.bin_path, 'easy_install') version = 'setuptools==0.6c11' if sys.platform != 'win32': return env.run(easy_install, version) - + tempdir = tempfile.mkdtemp() try: for f in glob.glob(easy_install+'*'): @@ -72,17 +84,19 @@ def install_setuptools(env): finally: shutil.rmtree(tempdir) -def reset_env(environ = None): + +def reset_env(environ=None): global env env = TestPipEnvironment(environ) - + return env env = None + class TestFailure(AssertionError): """ - + An "assertion" failed during testing. """ @@ -101,11 +115,12 @@ def _cleanup(): atexit.register(_cleanup) + class TestPipResult(object): def __init__(self, impl, verbose=False): self._impl = impl - + if verbose: print self.stdout if self.stderr: @@ -114,7 +129,7 @@ class TestPipResult(object): print '=======================' def __getattr__(self, attr): - return getattr(self._impl,attr) + return getattr(self._impl, attr) if sys.platform == 'win32': @property @@ -124,9 +139,9 @@ class TestPipResult(object): @property def stderr(self): return self._impl.stderr.replace('\r\n', '\n') - + def __str__(self): - return str(self._impl).replace('\r\n','\n') + return str(self._impl).replace('\r\n', '\n') else: # Python doesn't automatically forward __str__ through __getattr__ def __str__(self): @@ -140,49 +155,54 @@ class TestPipResult(object): egg_link_path = e.site_packages / pkg_name + '.egg-link' if without_egg_link: if egg_link_path in self.files_created: - raise TestFailure, 'unexpected egg link file created: %r\n%s' % (egg_link_path, self) + raise TestFailure('unexpected egg link file created: '\ + '%r\n%s' % (egg_link_path, self)) else: if not egg_link_path in self.files_created: - raise TestFailure, 'expected egg link file missing: %r\n%s' % (egg_link_path, self) + raise TestFailure('expected egg link file missing: '\ + '%r\n%s' % (egg_link_path, self)) egg_link_file = self.files_created[egg_link_path] if not (# FIXME: I don't understand why there's a trailing . here egg_link_file.bytes.endswith('.') and egg_link_file.bytes[:-1].strip().endswith(pkg_dir)): - raise TestFailure, textwrap.dedent(u'''\ + raise TestFailure(textwrap.dedent(u'''\ Incorrect egg_link file %r Expected ending: %r ------- Actual contents ------- %s -------------------------------''' % ( - egg_link_file, + egg_link_file, pkg_dir + u'\n.', - egg_link_file.bytes)) + egg_link_file.bytes))) pth_file = Path.string(e.site_packages / 'easy-install.pth') if (pth_file in self.files_updated) == without_egg_link: - raise TestFailure, '%r unexpectedly %supdated by install' % ( - pth_file, (not without_egg_link and 'not ' or '')) + raise TestFailure('%r unexpectedly %supdated by install' % ( + pth_file, (not without_egg_link and 'not ' or ''))) if (pkg_dir in self.files_created) == (curdir in without_files): - raise TestFailure, textwrap.dedent('''\ + raise TestFailure(textwrap.dedent('''\ expected package directory %r %sto be created actually created: %s ''') % ( - Path.string(pkg_dir), - (curdir in without_files and 'not ' or ''), - sorted(self.files_created.keys())) + Path.string(pkg_dir), + (curdir in without_files and 'not ' or ''), + sorted(self.files_created.keys()))) for f in with_files: if not (pkg_dir/f).normpath in self.files_created: - raise TestFailure, 'Package directory %r missing expected content %f' % (pkg_dir,f) + raise TestFailure('Package directory %r missing '\ + 'expected content %f' % (pkg_dir, f)) for f in without_files: if (pkg_dir/f).normpath in self.files_created: - raise TestFailure, 'Package directory %r has unexpected content %f' % (pkg_dir,f) + raise TestFailure('Package directory %r has '\ + 'unexpected content %f' % (pkg_dir, f)) + class TestPipEnvironment(TestFileEnvironment): """A specialized TestFileEnvironment for testing pip""" @@ -190,7 +210,7 @@ class TestPipEnvironment(TestFileEnvironment): # # Attribute naming convention # --------------------------- - # + # # Instances of this class have many attributes representing paths # in the filesystem. To keep things straight, absolute paths have # a name of the form xxxx_path and relative paths have a name that @@ -204,7 +224,7 @@ class TestPipEnvironment(TestFileEnvironment): # # Named with a leading dot to reduce the chance of spurious # results due to being mistaken for the virtualenv package. - venv = Path('.virtualenv') + venv = Path('.virtualenv') # The root of a directory tree to be used arbitrarily by tests scratch = Path('scratch') @@ -214,10 +234,10 @@ class TestPipEnvironment(TestFileEnvironment): verbose = False def __init__(self, environ=None): - + self.root_path = Path(tempfile.mkdtemp('-piptest')) - # We will set up a virtual environment at root_path. + # We will set up a virtual environment at root_path. self.scratch_path = self.root_path / self.scratch self.venv_path = self.root_path / self.venv @@ -230,11 +250,10 @@ class TestPipEnvironment(TestFileEnvironment): environ['PIP_NO_INPUT'] = '1' environ['PIP_LOG_FILE'] = str(self.root_path/'pip-log.txt') - super(TestPipEnvironment,self).__init__( - self.root_path, ignore_hidden=False, + super(TestPipEnvironment, self).__init__( + self.root_path, ignore_hidden=False, environ=environ, split_cmd=False, start_clear=False, - cwd=self.scratch_path, capture_temp=True, assert_no_temp=True - ) + cwd=self.scratch_path, capture_temp=True, assert_no_temp=True) demand_dirs(self.venv_path) demand_dirs(self.scratch_path) @@ -246,16 +265,16 @@ class TestPipEnvironment(TestFileEnvironment): assert self.venv_path == virtualenv_paths[0] # sanity check - for id,path in zip(('venv', 'lib', 'include', 'bin'), virtualenv_paths): + for id, path in zip(('venv', 'lib', 'include', 'bin'), virtualenv_paths): setattr(self, id+'_path', Path(path)) - setattr(self, id, relpath(self.root_path,path)) - + setattr(self, id, relpath(self.root_path, path)) + assert self.venv == TestPipEnvironment.venv # sanity check self.site_packages = self.lib/'site-packages' # put the test-scratch virtualenv's bin dir first on the PATH - self.environ['PATH'] = Path.pathsep.join( (self.bin_path, self.environ['PATH']) ) + self.environ['PATH'] = Path.pathsep.join((self.bin_path, self.environ['PATH'])) # test that test-scratch virtualenv creation produced sensible venv python result = self.run('python', '-c', 'import sys; print sys.executable') @@ -263,7 +282,7 @@ class TestPipEnvironment(TestFileEnvironment): if Path(pythonbin).noext != self.bin_path/'python': raise RuntimeError( - "Oops! 'python' in our test environment runs %r" + "Oops! 'python' in our test environment runs %r" " rather than expected %r" % (pythonbin, self.bin_path/'python')) # make sure we have current setuptools to avoid svn incompatibilities @@ -273,8 +292,8 @@ class TestPipEnvironment(TestFileEnvironment): # Uninstall whatever version of pip came with the virtualenv. # Earlier versions of pip were incapable of # self-uninstallation on Windows, so we use the one we're testing. - self.run('python', '-c', - 'import sys;sys.path.insert(0, %r);import pip;sys.exit(pip.main());' % os.path.dirname(here), + self.run('python', '-c', + 'import sys;sys.path.insert(0, %r);import pip;sys.exit(pip.main());' % os.path.dirname(here), 'uninstall', '-vvv', '-y', 'pip') # Install this version instead @@ -284,21 +303,23 @@ class TestPipEnvironment(TestFileEnvironment): if self.verbose: print '>> running', args, kw cwd = kw.pop('cwd', None) - run_from = kw.pop('run_from',None) + run_from = kw.pop('run_from', None) assert not cwd or not run_from, "Don't use run_from; it's going away" cwd = Path.string(cwd or run_from or self.cwd) - assert not isinstance(cwd,Path) - return TestPipResult( super(TestPipEnvironment,self).run(cwd=cwd,*args,**kw), verbose=self.verbose ) + assert not isinstance(cwd, Path) + return TestPipResult(super(TestPipEnvironment, self).run(cwd=cwd, *args, **kw), verbose=self.verbose) def __del__(self): shutil.rmtree(self.root_path, ignore_errors=True) + def run_pip(*args, **kw): return env.run('pip', *args, **kw) + def write_file(filename, text, dest=None): """Write a file in the dest (default=env.scratch_path) - + """ env = get_env() if dest: @@ -309,14 +330,17 @@ def write_file(filename, text, dest=None): f.write(text) f.close() + def mkdir(dirname): os.mkdir(os.path.join(get_env().scratch_path, dirname)) + def get_env(): if env is None: reset_env() return env + # FIXME ScriptTest does something similar, but only within a single # ProcResult; this generalizes it so states can be compared across # multiple commands. Maybe should be rolled into ScriptTest? @@ -345,12 +369,13 @@ def diff_states(start, end, ignore=None): """ ignore = ignore or [] + def prefix_match(path, prefix): - if path == prefix: + if path == prefix: return True prefix = prefix.rstrip(os.path.sep) + os.path.sep return path.startswith(prefix) - + start_keys = set([k for k in start.keys() if not any([prefix_match(k, i) for i in ignore])]) end_keys = set([k for k in end.keys() @@ -363,10 +388,11 @@ def diff_states(start, end, ignore=None): updated[k] = end[k] return dict(deleted=deleted, created=created, updated=updated) -def assert_all_changes( start_state, end_state, expected_changes ): + +def assert_all_changes(start_state, end_state, expected_changes): """ Fails if anything changed that isn't listed in the - expected_changes. + expected_changes. start_state is either a dict mapping paths to scripttest.[FoundFile|FoundDir] objects or a TestPipResult whose @@ -383,15 +409,16 @@ def assert_all_changes( start_state, end_state, expected_changes ): if isinstance(end_state, TestPipResult): end_files = end_state.files_after - diff = diff_states( start_files, end_files, ignore=expected_changes ) - if diff.values() != [{},{},{}]: + diff = diff_states(start_files, end_files, ignore=expected_changes) + if diff.values() != [{}, {}, {}]: import pprint - raise TestFailure, 'Unexpected changes:\n' + '\n'.join( - [k + ': ' + ', '.join(v.keys()) for k,v in diff.items()]) + raise TestFailure('Unexpected changes:\n' + '\n'.join( + [k + ': ' + ', '.join(v.keys()) for k, v in diff.items()])) # Don't throw away this potentially useful information return diff + if __name__ == '__main__': sys.stderr.write("Run pip's tests using nosetests. Requires virtualenv, ScriptTest, and nose.\n") sys.exit(1) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 9790e8aa1..b8e47679b 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -4,7 +4,9 @@ Tests for the proxy support in pip. TODO shouldn't need to hack sys.path in here. """ -import os, sys + +import os +import sys sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import os @@ -13,22 +15,25 @@ import getpass from pip.basecommand import get_proxy from test_pip import here + def new_getpass(prompt, answer='passwd'): print '%s%s' % (prompt, answer) return answer + def test_correct_pip_version(): """ Check we are importing pip from the right place. - + """ base = os.path.dirname(here) assert pip.__file__.startswith(base), pip.__file__ + def test_remove_proxy(): """ Test removing proxy from environ. - + """ if 'HTTP_PROXY' in os.environ: del os.environ['HTTP_PROXY'] @@ -40,10 +45,11 @@ def test_remove_proxy(): assert get_proxy('server.com:80') == 'server.com:80' assert get_proxy('user:passwd@server.com:3128') == 'user:passwd@server.com:3128' + def test_get_proxy(): """ Test get_proxy returns correct proxy info. - + """ # monkeypatch getpass.getpass, to avoid asking for a password old_getpass = getpass.getpass diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 705262236..3063a7be6 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -3,10 +3,11 @@ import os import textwrap from test_pip import reset_env, run_pip, write_file, pyversion + def test_requirements_file(): """ Test installing from a requirements file. - + """ env = reset_env() write_file('initools-req.txt', textwrap.dedent("""\ @@ -20,10 +21,11 @@ def test_requirements_file(): assert result.files_created[env.site_packages/'simplejson'].dir assert result.files_created[env.site_packages/'simplejson-1.7.4-py%s.egg-info' % pyversion].dir + def test_multiple_requirements_files(): """ Test installing from multiple nested requirements files. - + """ env = reset_env() write_file('initools-req.txt', textwrap.dedent("""\ diff --git a/tests/test_search.py b/tests/test_search.py index b04c913a8..f9c80d5d6 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1,5 +1,6 @@ -import os, sys +import os +import sys sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) from pip.commands.search import compare_versions, highest_version, transform_hits @@ -9,7 +10,7 @@ from test_pip import run_pip, reset_env def test_version_compare(): """ Test version comparison. - + """ assert compare_versions('1.0', '1.1') == -1 assert compare_versions('1.1', '1.0') == 1 @@ -17,10 +18,11 @@ def test_version_compare(): assert highest_version(['1.0', '2.0', '0.1']) == '2.0' assert highest_version(['1.0a1', '1.0']) == '1.0' + def test_pypi_xml_transformation(): """ Test transformation of data structures (pypi xmlrpc to custom list). - + """ pypi_hits = [{'_pypi_ordering': 100, 'name': 'foo', 'summary': 'foo summary', 'version': '1.0'}, {'_pypi_ordering': 200, 'name': 'foo', 'summary': 'foo summary v2', 'version': '2.0'}, @@ -29,10 +31,11 @@ def test_pypi_xml_transformation(): {'score': 50, 'versions': ['1.0'], 'name': 'bar', 'summary': 'bar summary'}] assert expected == transform_hits(pypi_hits) + def test_search(): """ End to end test of search command. - + """ reset_env() output = run_pip('search', 'pip', expect_error=True) diff --git a/tests/test_uninstall.py b/tests/test_uninstall.py index 9c956cc24..160db8d75 100644 --- a/tests/test_uninstall.py +++ b/tests/test_uninstall.py @@ -1,15 +1,17 @@ -import textwrap, sys +import textwrap +import sys from os.path import join from tempfile import mkdtemp from test_pip import here, reset_env, run_pip, get_env, assert_all_changes, write_file from path import Path import pprint + def test_simple_uninstall(): """ Test simple install and uninstall. - + """ env = reset_env() result = run_pip('install', 'INITools==0.2', expect_error=True) @@ -17,14 +19,11 @@ def test_simple_uninstall(): result2 = run_pip('uninstall', 'INITools', '-y', expect_error=True) assert_all_changes(result, result2, [env.venv/'build', 'cache']) + def test_uninstall_with_scripts(): """ Uninstall an easy_installed package with scripts. - - """ - """ - Uninstall an easy_installed package with scripts. - + """ env = reset_env() result = env.run('easy_install', 'PyLogo') @@ -34,11 +33,12 @@ def test_uninstall_with_scripts(): result2 = run_pip('uninstall', 'pylogo', '-y', expect_error=True) assert_all_changes(result, result2, [env.venv/'build', 'cache']) + def test_uninstall_namespace_package(): """ Uninstall a distribution with a namespace package without clobbering the namespace and everything in it. - + """ env = reset_env() result = run_pip('install', 'pd.requires==0.0.3', expect_error=True) @@ -47,10 +47,11 @@ def test_uninstall_namespace_package(): assert join(env.site_packages, 'pd') not in result2.files_deleted, sorted(result2.files_deleted.keys()) assert join(env.site_packages, 'pd', 'find') in result2.files_deleted, sorted(result2.files_deleted.keys()) + def test_uninstall_console_scripts(): """ Test uninstalling a package with more files (console_script entry points, extra directories). - + """ env = reset_env() result = run_pip('install', 'virtualenv', expect_error=True) @@ -58,10 +59,11 @@ def test_uninstall_console_scripts(): result2 = run_pip('uninstall', 'virtualenv', '-y', expect_error=True) assert_all_changes(result, result2, [env.venv/'build', 'cache']) + def test_uninstall_easy_installed_console_scripts(): """ Test uninstalling package with console_scripts that is easy_installed. - + """ env = reset_env() result = env.run('easy_install', 'virtualenv') @@ -69,10 +71,11 @@ def test_uninstall_easy_installed_console_scripts(): result2 = run_pip('uninstall', 'virtualenv', '-y') assert_all_changes(result, result2, [env.venv/'build', 'cache']) + def test_uninstall_editable_from_svn(): """ Test uninstalling an editable installation from svn. - + """ env = reset_env() result = run_pip('install', '-e', 'svn+http://svn.colorstudy.com/INITools/trunk#egg=initools-dev') @@ -81,11 +84,11 @@ def test_uninstall_editable_from_svn(): assert (env.venv/'src'/'initools' in result2.files_after), 'oh noes, pip deleted my sources!' assert_all_changes(result, result2, [env.venv/'src', env.venv/'build']) - + def test_uninstall_editable_with_source_outside_venv(): """ Test uninstalling editable install from existing source outside the venv. - + """ tmpdir = join(mkdtemp(), 'virtualenv') env = reset_env() @@ -94,11 +97,12 @@ def test_uninstall_editable_with_source_outside_venv(): assert (join(env.site_packages, 'virtualenv.egg-link') in result2.files_created), result2.files_created.keys() result3 = run_pip('uninstall', '-y', 'virtualenv', expect_error=True) assert_all_changes(result, result3, [env.venv/'build']) - + + def test_uninstall_from_reqs_file(): """ Test uninstall from a requirements file. - + """ env = reset_env() write_file('test-req.txt', textwrap.dedent("""\ @@ -112,7 +116,7 @@ def test_uninstall_from_reqs_file(): -f http://www.example.com -i http://www.example.com --extra-index-url http://www.example.com - + -e svn+http://svn.colorstudy.com/INITools/trunk#egg=initools-dev # and something else to test out: PyLogo<0.4 diff --git a/tests/test_upgrade.py b/tests/test_upgrade.py index 0bff6c9ae..33787b97e 100644 --- a/tests/test_upgrade.py +++ b/tests/test_upgrade.py @@ -3,6 +3,7 @@ from os.path import join import textwrap from test_pip import here, reset_env, run_pip, get_env, assert_all_changes, write_file + def test_no_upgrade_unless_requested(): """ No upgrade if not specifically requested. @@ -13,6 +14,7 @@ def test_no_upgrade_unless_requested(): result2 = run_pip('install', 'INITools', expect_error=True) assert not result2.files_created, 'pip install INITools upgraded when it should not have' + def test_upgrade_to_specific_version(): """ It does upgrade to specific version requested. @@ -23,6 +25,7 @@ def test_upgrade_to_specific_version(): result2 = run_pip('install', 'INITools==0.2', expect_error=True) assert result2.files_created, 'pip install with specific version did not upgrade' + def test_upgrade_if_requested(): """ And it does upgrade if requested. @@ -33,6 +36,7 @@ def test_upgrade_if_requested(): result2 = run_pip('install', '--upgrade', 'INITools', expect_error=True) assert result2.files_created, 'pip install --upgrade did not upgrade' + def test_uninstall_before_upgrade(): """ Automatic uninstall-before-upgrade. @@ -46,6 +50,7 @@ def test_uninstall_before_upgrade(): result3 = run_pip('uninstall', 'initools', '-y', expect_error=True) assert_all_changes(result, result3, [env.venv/'build', 'cache']) + def test_upgrade_from_reqs_file(): """ Upgrade from a requirements file. @@ -67,6 +72,7 @@ def test_upgrade_from_reqs_file(): result3 = run_pip('uninstall', '-r', env.scratch_path/ 'test-req.txt', '-y') assert_all_changes(result, result3, [env.venv/'build', 'cache', env.scratch/'test-req.txt']) + def test_uninstall_rollback(): """ Test uninstall-rollback (using test package with a setup.py @@ -79,6 +85,6 @@ def test_uninstall_rollback(): assert env.site_packages / 'broken.py' in result.files_created, result.files_created.keys() result2 = run_pip('install', '-f', find_links, '--no-index', 'broken==0.2broken', expect_error=True) assert result2.returncode == 1, str(result2) - env.run( 'python', '-c', "import broken; print broken.VERSION").stdout + env.run('python', '-c', "import broken; print broken.VERSION").stdout '0.1\n' assert_all_changes(result.files_after, result2, [env.venv/'build', 'pip-log.txt'])