Repository: pypa/pip

Issue:
AttributeError: Values instance has no attribute 'find_links'
**Environment**

* pip version: pip 10.0.1 from c:\python27\lib\site-packages\pip (python 2.7)
* Python version: 2.7.14
* OS: Windows 10

**Description**
```
$ pip config list --verbose
For variant 'global', will try loading 'C:\ProgramData\pip\pip.ini'
For variant 'user', will try loading 'C:\Users\<user>\pip\pip.ini'
For variant 'user', will try loading 'C:\Users\<user>\AppData\Roaming\pip\pip.ini'
global.cert='C:\\ProgramData\\pip\\ca-bundle.crt'
global.index-url='...........'
install.no-binary='.......'
There was an error checking the latest version of pip
Traceback (most recent call last):
  File "c:\python27\lib\site-packages\pip\_internal\utils\outdated.py", line 125, in pip_version_check
    find_links=options.find_links,
AttributeError: Values instance has no attribute 'find_links'
```

**Expected behavior**
No exception

**How to Reproduce**
1. Execute `pip config --list --verbose`


Fix the pip version check when using --no-cache-dir
<!---
Thank you for your soon to be pull request. Before you submit this, please
double check to make sure that you've added a news file fragment. In pip we
generate our NEWS.rst from multiple news fragment files, and all pull requests
require either a news file fragment or a marker to indicate they don't require
one.

To read more about adding a news file fragment for your PR, please check out
our documentation at: https://pip.pypa.io/en/latest/development/contributing/#adding-a-news-entry
-->
The pip version check will now work correctly when using ``--no-cache-dir``.

Fixes #5679


Discussion:
I can confirm on Linux also
pip -vvv config
Need an action (edit, get, list, set, unset) to perform.
There was an error checking the latest version of pip
Traceback (most recent call last):
  File "/opt/our_custom_path/lib/python2.7/site-packages/pip/_internal/utils/outdated.py", line 125, in pip_version_check
    find_links=options.find_links,
AttributeError: Values instance has no attribute 'find_links'
pip -V
pip 10.0.1 from /opt/our_custom_path/lib/python2.7/site-packages/pip (python 2.7)

I confirm :+1: 
Thanks for the report.
I just tried this on a fresh Ubuntu 14.04 system, I didn't get any errors.

Could someone explain how to reproduce this? 
There is something strange here:
I'm only getting the error with my system's pip (on archlinux) and not in a fresh venv, but the only diff between the two are in the "pip/_vendor" which is unlikely to explain the difference...
Ok: in a fresh venv we are using a different file for `pip-selfcheck.json` (https://github.com/pypa/pip/blob/10.0.1/src/pip/_internal/utils/outdated.py#L26) and not the global one (https://github.com/pypa/pip/blob/10.0.1/src/pip/_internal/utils/outdated.py#L51).
@pradyunsg : removing the `pip-selfcheck.json` should trigger the issue
@xavfernandez FWIW, we simplified the two file mechanism in #5419 -- that might make this harder to reproduce on `master`. Is this still an issue on `master`?

I've tried on a pyenv installed Python 3.7 on MacOS and it doesn't seem to be erroring for me, in a venv or outside of it. I have empty configuration files however.

Could you provide the exact config file contents that is causing this? :/
@pradyunsg the issue still exists on `master`.

You don't need a config file, you only need to end up here: https://github.com/pypa/pip/blob/96f9fcc/src/pip/_internal/utils/outdated.py#L103 (so either an old date in `selfcheck.json` or no `selfcheck.json` at all).

The issue being that `options` won't contain a `find_links`  key which is expected since this option is only present in [index_group](https://github.com/pypa/pip/blob/96f9fcc4a02a1e7521d807899a2723b31559cdde/src/pip/_internal/cmdoptions.py#L610) which in turn is only available in `wheel`, `install`, `list` and `download` commands.

Thus we get similar traceback for `freeze`:
<details>

```
$ pip freeze -v
Created temporary directory: /tmp/pip-ephem-wheel-cache-e138jnjj
There was an error checking the latest version of pip
Traceback (most recent call last):
  File "/home/xfernandez/.virtualenvs/tmp-475844793f0ebbc/lib/python3.6/site-packages/pip/_internal/utils/outdated.py", line 107, in pip_version_check
    index_urls=[options.index_url] + options.extra_index_urls,
AttributeError: 'Values' object has no attribute 'index_url'
```
</details>

and `show`:
<details>

```
$ pip show pip -v
Name: pip
Version: 18.0.dev0
Summary: The PyPA recommended tool for installing Python packages.
Home-page: https://pip.pypa.io/
Author: The pip developers
Author-email: pypa-dev@groups.google.com
License: MIT
Location: /home/xfernandez/.virtualenvs/tmp-475844793f0ebbc/lib/python3.6/site-packages
Requires: 
Required-by: 
Metadata-Version: 2.1
Installer: pip
Classifiers:
  Development Status :: 5 - Production/Stable
  Intended Audience :: Developers
  License :: OSI Approved :: MIT License
  Topic :: Software Development :: Build Tools
  Programming Language :: Python
  Programming Language :: Python :: 2
  Programming Language :: Python :: 2.7
  Programming Language :: Python :: 3
  Programming Language :: Python :: 3.4
  Programming Language :: Python :: 3.5
  Programming Language :: Python :: 3.6
  Programming Language :: Python :: Implementation :: CPython
  Programming Language :: Python :: Implementation :: PyPy
Entry-points:
  [console_scripts]
  pip = pip._internal:main
  pip3 = pip._internal:main
  pip3.6 = pip._internal:main
There was an error checking the latest version of pip
Traceback (most recent call last):
  File "/home/xfernandez/.virtualenvs/tmp-475844793f0ebbc/lib/python3.6/site-packages/pip/_internal/utils/outdated.py", line 106, in pip_version_check
    find_links=options.find_links,
AttributeError: 'Values' object has no attribute 'find_links'
```
</details>

Maybe we should restrict pip selfcheck to commands with `index_group` options available ?
Ah. Right. So, that seems like an issue with the self check logic.

Your suggestion makes sense to me. It'll be an interesting task to implement it though. ;)

The self-checking logic is strange.

1. It uses one global state file outside of `virtualenv` for caching `pip` version checks.
2. It uses PackageFinder component where it could just query some URL with latest tag.
3. It checks `installed version` instead of checking itself.
Added request to PyPI to simplify lookups by using just a single request- https://github.com/pypa/warehouse/issues/4663
Reproduced this. Working on this.
Could this patch please be merged? It fixes two of the three tracebacks reported in #5679 

I wasn't able to reproduce https://github.com/pypa/pip/issues/5679#issuecomment-409174290 . @segevfiner could you share how to reproduce that from scratch?
> I wasn't able to reproduce #5679 (comment) . @segevfiner could you share how to reproduce that from scratch?

`pip show --verbose --no-cache-dir requests` after merging this.

Resolving patch:
diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py
--- a/src/pip/_internal/cli/base_command.py
+++ b/src/pip/_internal/cli/base_command.py
@@ -170,12 +170,14 @@ def main(self, args):
 
             return UNKNOWN_ERROR
         finally:
-            # Check if we're using the latest version of pip available
-            skip_version_check = (
-                options.disable_pip_version_check or
-                getattr(options, "no_index", False)
+            allow_version_check = (
+                # Does this command have the index_group options?
+                hasattr(options, "no_index") and
+                # Is this command allowed to perform this check?
+                not (options.disable_pip_version_check or options.no_index)
             )
-            if not skip_version_check:
+            # Check if we're using the latest version of pip available
+            if allow_version_check:
                 session = self._build_session(
                     options,
                     retries=0,
diff --git a/src/pip/_internal/utils/outdated.py b/src/pip/_internal/utils/outdated.py
--- a/src/pip/_internal/utils/outdated.py
+++ b/src/pip/_internal/utils/outdated.py
@@ -22,16 +22,25 @@
 
 class SelfCheckState(object):
     def __init__(self, cache_dir):
-        self.statefile_path = os.path.join(cache_dir, "selfcheck.json")
+        self.state = {}
+        self.statefile_path = None
 
-        # Load the existing state
-        try:
-            with open(self.statefile_path) as statefile:
-                self.state = json.load(statefile)[sys.prefix]
-        except (IOError, ValueError, KeyError):
-            self.state = {}
+        # Try to load the existing state
+        if cache_dir:
+            self.statefile_path = os.path.join(cache_dir, "selfcheck.json")
+            try:
+                with open(self.statefile_path) as statefile:
+                    self.state = json.load(statefile)[sys.prefix]
+            except (IOError, ValueError, KeyError):
+                # Explicitly suppressing exceptions, since we don't want to
+                # error out if the cache file is invalid.
+                pass
 
     def save(self, pypi_version, current_time):
+        # If we do not have a path to cache in, don't bother saving.
+        if not self.statefile_path:
+            return
+
         # Check to make sure that we own the directory
         if not check_path_owner(os.path.dirname(self.statefile_path)):
             return


Test patch:
diff --git a/tests/unit/test_unit_outdated.py b/tests/unit/test_unit_outdated.py
--- a/tests/unit/test_unit_outdated.py
+++ b/tests/unit/test_unit_outdated.py
@@ -169,3 +169,9 @@ def fake_lock(filename):
 
     # json.dumps will call this a number of times
     assert len(fake_file.write.calls)
+
+
+def test_self_check_state_no_cache_dir():
+    state = outdated.SelfCheckState(cache_dir=False)
+    assert state.state == {}
+    assert state.statefile_path is None