Skip to content

Only install stable releases by default #834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 12, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: python
python:
- 2.5
- 2.6
- 2.7
- 3.2
Expand Down
17 changes: 17 additions & 0 deletions docs/logic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ Some Examples::
SomethingWhoseVersionIDontCareAbout


.. _`Pre Release Versions`:

Pre Release Versions
====================

By default pip will only install stable versions as specified by PEP426. If
a version cannot be parsed as a compliant PEP426 version then it is assumed
to be stable.

If a Requirement specifier includes a pre release version (e.g. ``>=0.0.dev0``) then
pip will allow pre-release versions for that requirement. This does not include
the != flag.

The ``pip install`` command also supports a ``--pre`` flag that will enable
installing pre-releases.


.. _`VCS Support`:

VCS Support
Expand Down
8 changes: 7 additions & 1 deletion pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ def __init__(self, *args, **kw):
default=None,
help="Install everything relative to this alternate root directory.")

cmd_opts.add_option(
'--pre',
action='store_true',
default=False,
help="Include pre-releases in the available versions.")

index_opts = make_option_group(index_group, self.parser)

self.parser.insert_option_group(0, index_opts)
Expand Down Expand Up @@ -232,7 +238,7 @@ def run(self, options, args):
use_user_site=options.use_user_site)
for name in args:
requirement_set.add_requirement(
InstallRequirement.from_line(name, None))
InstallRequirement.from_line(name, None, prereleases=options.pre))
for name in options.editables:
requirement_set.add_requirement(
InstallRequirement.from_editable(name, default_vcs=options.default_vcs))
Expand Down
5 changes: 4 additions & 1 deletion pip/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import dummy_threading as threading

from pip.log import logger
from pip.util import Inf, normalize_name, splitext
from pip.util import Inf, normalize_name, splitext, is_prerelease
from pip.exceptions import DistributionNotFound, BestVersionAlreadyInstalled
from pip.backwardcompat import (WindowsError, BytesIO,
Queue, urlparse,
Expand Down Expand Up @@ -183,6 +183,9 @@ def mkurl_pypi_url(url):
logger.info("Ignoring link %s, version %s doesn't match %s"
% (link, version, ','.join([''.join(s) for s in req.req.specs])))
continue
elif is_prerelease(version) and not req.prereleases:
logger.info("Ignoring link %s, version %s is a pre-release (use --pre to allow)." % (link, version))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize I'm way, way, behind on this, but was there any discussion of making this logged info loud at the default verbosity level (i.e. warning rather than info), at least for 1.4, since it's a significant change in behavior that might confuse people if it happens silently?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't around when this landed, but I agree, we should be loud about it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I remember. I'm not opposed to that change though. Might be rather noisy though since it'll give a warning for every ==dev and such for any apps that have them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, that could get noisy. I guess what we'd ideally want is just a single warning iff a stable version is selected when a prerelease version would otherwise have been selected. But that's a more complex fix than just changing this info to warning.

continue
applicable_versions.append((parsed_version, link, version))
#bring the latest version to the front, but maintains the priority ordering as secondary
applicable_versions = sorted(applicable_versions, key=lambda v: v[0], reverse=True)
Expand Down
20 changes: 14 additions & 6 deletions pip/req.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
is_installable_dir, is_local, dist_is_local,
dist_in_usersite, dist_in_site_packages, renames,
normalize_path, egg_link_path, make_path_relative,
call_subprocess)
call_subprocess, is_prerelease)
from pip.backwardcompat import (urlparse, urllib, uses_pycache,
ConfigParser, string_types, HTTPError,
get_python_version, b)
Expand All @@ -37,7 +37,7 @@
class InstallRequirement(object):

def __init__(self, req, comes_from, source_dir=None, editable=False,
url=None, as_egg=False, update=True):
url=None, as_egg=False, update=True, prereleases=None):
self.extras = ()
if isinstance(req, string_types):
req = pkg_resources.Requirement.parse(req)
Expand Down Expand Up @@ -65,6 +65,14 @@ def __init__(self, req, comes_from, source_dir=None, editable=False,
self.uninstalled = None
self.use_user_site = False

# True if pre-releases are acceptable
if prereleases:
self.prereleases = True
elif self.req is not None:
self.prereleases = any([is_prerelease(x[1]) and x[0] != "!=" for x in self.req.specs])
else:
self.prereleases = False

@classmethod
def from_editable(cls, editable_req, comes_from=None, default_vcs=None):
name, url, extras_override = parse_editable(editable_req, default_vcs)
Expand All @@ -73,15 +81,15 @@ def from_editable(cls, editable_req, comes_from=None, default_vcs=None):
else:
source_dir = None

res = cls(name, comes_from, source_dir=source_dir, editable=True, url=url)
res = cls(name, comes_from, source_dir=source_dir, editable=True, url=url, prereleases=True)

if extras_override is not None:
res.extras = extras_override

return res

@classmethod
def from_line(cls, name, comes_from=None):
def from_line(cls, name, comes_from=None, prereleases=None):
"""Creates an InstallRequirement from a name, which might be a
requirement, directory containing 'setup.py', filename, or URL.
"""
Expand Down Expand Up @@ -115,7 +123,7 @@ def from_line(cls, name, comes_from=None):
else:
req = name

return cls(req, comes_from, url=url)
return cls(req, comes_from, url=url, prereleases=prereleases)

def __str__(self):
if self.req:
Expand Down Expand Up @@ -1353,7 +1361,7 @@ def parse_requirements(filename, finder=None, comes_from=None, options=None):
req = InstallRequirement.from_editable(
line, comes_from=comes_from, default_vcs=options.default_vcs)
else:
req = InstallRequirement.from_line(line, comes_from)
req = InstallRequirement.from_line(line, comes_from, prereleases=getattr(options, "pre", None))
yield req


Expand Down
18 changes: 18 additions & 0 deletions pip/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
console_to_str, user_site, ssl)
from pip.locations import site_packages, running_under_virtualenv, virtualenv_no_global
from pip.log import logger
from pip.vendor.distlib import version

__all__ = ['rmtree', 'display_path', 'backup_dir',
'find_command', 'ask', 'Inf',
Expand Down Expand Up @@ -666,3 +667,20 @@ def call_subprocess(cmd, show_stdout=True,
% (command_desc, proc.returncode, cwd))
if stdout is not None:
return ''.join(all_output)


def is_prerelease(vers):
"""
Attempt to determine if this is a pre-release using PEP386/PEP426 rules.

Will return True if it is a pre-release, False is not, and None if we cannot
determine.
"""
normalized = version.suggest_normalized_version(vers)

if normalized is None:
# Cannot normalize
return

parsed = version.normalized_key(normalized)
return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed])
12 changes: 12 additions & 0 deletions pip/vendor/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
all: clean vendor

clean:
@# Delete vendored items
for i in `find . -maxdepth 1 -type d -print`; do rm -rf $i; done

vendor:
@# Install vendored libraries
pip install -t . -r vendor.txt

@# Cleanup .egg-info directories
rm -rf *.egg-info
7 changes: 7 additions & 0 deletions pip/vendor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
pip.vendor is for vendoring dependencies of pip to prevent needing pip to
depend on something external.

Files inside of pip.vendor should be considered immutable and should only be
updated to versions from upstream.
"""
22 changes: 22 additions & 0 deletions pip/vendor/distlib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2013 Vinay Sajip.
# Licensed to the Python Software Foundation under a contributor agreement.
# See LICENSE.txt and CONTRIBUTORS.txt.
#
import logging

__version__ = '0.1.0'

class DistlibException(Exception):
pass

try:
from logging import NullHandler
except ImportError: # pragma: no cover
class NullHandler(logging.Handler):
def handle(self, record): pass
def emit(self, record): pass

logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
6 changes: 6 additions & 0 deletions pip/vendor/distlib/_backport/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Modules copied from Python 3 standard libraries, for internal use only.
Individual classes and functions are found in d2._backport.misc. Intended
usage is to always import things missing from 3.1 from that module: the
built-in/stdlib objects will be used if found.
"""
41 changes: 41 additions & 0 deletions pip/vendor/distlib/_backport/misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 The Python Software Foundation.
# See LICENSE.txt and CONTRIBUTORS.txt.
#
"""Backports for individual classes and functions."""

import os
import sys

__all__ = ['cache_from_source', 'callable', 'fsencode']


try:
from imp import cache_from_source
except ImportError:
def cache_from_source(py_file, debug=__debug__):
ext = debug and 'c' or 'o'
return py_file + ext


try:
callable = callable
except NameError:
from collections import Callable

def callable(obj):
return isinstance(obj, Callable)


try:
fsencode = os.fsencode
except AttributeError:
def fsencode(filename):
if isinstance(filename, bytes):
return filename
elif isinstance(filename, str):
return filename.encode(sys.getfilesystemencoding())
else:
raise TypeError("expect bytes or str, not %s" %
type(filename).__name__)
Loading