Repository: pypa/pip

Issue:
pip list outdated
I install Flask version 0.8 with pip in editable mode 

`pip install -e git+https://github.com/mitsuhiko/flask.git@0.8#egg=flask`

Output of pip list 

```
argparse (1.2.1)
Flask (0.8dev-20130506, /home/pratz/VirtualEnv/learnflask/src/flask)
Jinja2 (2.6)
Werkzeug (0.8.3)
wsgiref (0.1.2)
```

Ouput of pip list -o
Nothing ( returns to console without any output )

Should not the output of pip list -o be
`Flask (Current: 0.8dev Latest: 0.9)`

Or is this how pip list -o works for editable mode ?

NOTE: pip version is 1.3.1



Discussion:
[Editable packages are not included in the outdated list](http://www.pip-installer.org/en/1.3.1/usage.html#id14). I don't know whether there is an issue open for that, because I would like that functionality (but it's not an easy one to spec).

This comes from #752 but I see no reason the options `--outdated` and `--editable` could not be combined.


Resolving patch:
diff --git a/pip/commands/list.py b/pip/commands/list.py
--- a/pip/commands/list.py
+++ b/pip/commands/list.py
@@ -4,7 +4,7 @@
 import warnings
 
 from pip.basecommand import Command
-from pip.exceptions import DistributionNotFound
+from pip.exceptions import CommandError, DistributionNotFound
 from pip.index import FormatControl, fmt_ctl_formats, PackageFinder, Search
 from pip.req import InstallRequirement
 from pip.utils import (
@@ -37,12 +37,12 @@ def __init__(self, *args, **kw):
             '-o', '--outdated',
             action='store_true',
             default=False,
-            help='List outdated packages (excluding editables)')
+            help='List outdated packages')
         cmd_opts.add_option(
             '-u', '--uptodate',
             action='store_true',
             default=False,
-            help='List uptodate packages (excluding editables)')
+            help='List uptodate packages')
         cmd_opts.add_option(
             '-e', '--editable',
             action='store_true',
@@ -112,22 +112,25 @@ def run(self, options, args):
                 "no longer has any effect.",
                 RemovedInPip10Warning,
             )
+        if options.outdated and options.uptodate:
+            raise CommandError(
+                "Options --outdated and --uptodate cannot be combined.")
 
         if options.outdated:
             self.run_outdated(options)
         elif options.uptodate:
             self.run_uptodate(options)
-        elif options.editable:
-            self.run_editables(options)
         else:
             self.run_listing(options)
 
     def run_outdated(self, options):
-        for dist, version, typ in self.find_packages_latest_versions(options):
-            if version > dist.parsed_version:
+        for dist, latest_version, typ in sorted(
+                self.find_packages_latest_versions(options),
+                key=lambda p: p[0].project_name.lower()):
+            if latest_version > dist.parsed_version:
                 logger.info(
-                    '%s (Current: %s Latest: %s [%s])',
-                    dist.project_name, dist.version, version, typ,
+                    '%s - Latest: %s [%s]',
+                    self.output_package(dist), latest_version, typ,
                 )
 
     def find_packages_latest_versions(self, options):
@@ -137,8 +140,10 @@ def find_packages_latest_versions(self, options):
             index_urls = []
 
         dependency_links = []
-        for dist in get_installed_distributions(local_only=options.local,
-                                                user_only=options.user):
+        for dist in get_installed_distributions(
+                local_only=options.local,
+                user_only=options.user,
+                editables_only=options.editable):
             if dist.has_metadata('dependency_links.txt'):
                 dependency_links.extend(
                     dist.get_metadata_lines('dependency_links.txt'),
@@ -151,7 +156,7 @@ def find_packages_latest_versions(self, options):
             installed_packages = get_installed_distributions(
                 local_only=options.local,
                 user_only=options.user,
-                include_editables=False,
+                editables_only=options.editable,
             )
             format_control = FormatControl(set(), set())
             wheel_cache = WheelCache(options.cache_dir, format_control)
@@ -189,16 +194,19 @@ def run_listing(self, options):
         installed_packages = get_installed_distributions(
             local_only=options.local,
             user_only=options.user,
+            editables_only=options.editable,
         )
         self.output_package_listing(installed_packages)
 
-    def run_editables(self, options):
-        installed_packages = get_installed_distributions(
-            local_only=options.local,
-            user_only=options.user,
-            editables_only=True,
-        )
-        self.output_package_listing(installed_packages)
+    def output_package(self, dist):
+        if dist_is_editable(dist):
+            return '%s (%s, %s)' % (
+                dist.project_name,
+                dist.version,
+                dist.location,
+            )
+        else:
+            return '%s (%s)' % (dist.project_name, dist.version)
 
     def output_package_listing(self, installed_packages):
         installed_packages = sorted(
@@ -206,15 +214,7 @@ def output_package_listing(self, installed_packages):
             key=lambda dist: dist.project_name.lower(),
         )
         for dist in installed_packages:
-            if dist_is_editable(dist):
-                line = '%s (%s, %s)' % (
-                    dist.project_name,
-                    dist.version,
-                    dist.location,
-                )
-            else:
-                line = '%s (%s)' % (dist.project_name, dist.version)
-            logger.info(line)
+            logger.info(self.output_package(dist))
 
     def run_uptodate(self, options):
         uptodate = []


Test patch:
diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py
--- a/tests/functional/test_list.py
+++ b/tests/functional/test_list.py
@@ -59,7 +59,7 @@ def test_uptodate_flag(script, data):
         expect_stderr=True,
     )
     assert 'simple (1.0)' not in result.stdout  # 3.0 is latest
-    assert 'pip-test-package' not in result.stdout  # editables excluded
+    assert 'pip-test-package (0.1.1,' in result.stdout  # editables included
     assert 'simple2 (3.0)' in result.stdout, str(result)
 
 
@@ -75,15 +75,17 @@ def test_outdated_flag(script, data):
     )
     script.pip(
         'install', '-e',
-        'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package'
+        'git+https://github.com/pypa/pip-test-package.git'
+        '@0.1#egg=pip-test-package'
     )
     result = script.pip(
         'list', '-f', data.find_links, '--no-index', '--outdated',
         expect_stderr=True,
     )
-    assert 'simple (Current: 1.0 Latest: 3.0 [sdist])' in result.stdout
-    assert 'simplewheel (Current: 1.0 Latest: 2.0 [wheel])' in result.stdout
-    assert 'pip-test-package' not in result.stdout  # editables excluded
+    assert 'simple (1.0) - Latest: 3.0 [sdist]' in result.stdout
+    assert 'simplewheel (1.0) - Latest: 2.0 [wheel]' in result.stdout
+    assert 'pip-test-package (0.1, ' in result.stdout
+    assert ' Latest: 0.1.1 [sdist]' in result.stdout
     assert 'simple2' not in result.stdout, str(result)  # 3.0 is latest
 
 
@@ -102,3 +104,46 @@ def test_editables_flag(script, data):
     assert os.path.join('src', 'pip-test-package') in result.stdout, (
         str(result)
     )
+
+
+@pytest.mark.network
+def test_uptodate_editables_flag(script, data):
+    """
+    test the behavior of --editable --uptodate flag in the list command
+    """
+    script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0')
+    result = script.pip(
+        'install', '-e',
+        'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package'
+    )
+    result = script.pip(
+        'list', '-f', data.find_links, '--no-index',
+        '--editable', '--uptodate',
+        expect_stderr=True,
+    )
+    assert 'simple (1.0)' not in result.stdout, str(result)
+    assert os.path.join('src', 'pip-test-package') in result.stdout, (
+        str(result)
+    )
+
+
+@pytest.mark.network
+def test_outdated_editables_flag(script, data):
+    """
+    test the behavior of --editable --outdated flag in the list command
+    """
+    script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0')
+    result = script.pip(
+        'install', '-e',
+        'git+https://github.com/pypa/pip-test-package.git'
+        '@0.1#egg=pip-test-package'
+    )
+    result = script.pip(
+        'list', '-f', data.find_links, '--no-index',
+        '--editable', '--outdated',
+        expect_stderr=True,
+    )
+    assert 'simple (1.0)' not in result.stdout, str(result)
+    assert os.path.join('src', 'pip-test-package') in result.stdout, (
+        str(result)
+    )