Repository: conda/conda

Issue:
Cannot downgrade conda from 4.7
## Current Behavior
After updating conda to 4.7.5, conda can no longer be downgraded to a previous version using a version spec such as `conda install conda=4.6`.  This behavior can be changed if the `auto_update_conda` configuration parameter is set to false.


### Steps to Reproduce
```
[root@chi9 build_scripts]# conda --version
conda 4.6.14
[root@chi9 build_scripts]# conda update -y -q conda conda-build
Collecting package metadata: ...working... done
Solving environment: ...working... done

## Package Plan ##

  environment location: /opt/conda

  added / updated specs:
    - conda
    - conda-build


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    certifi-2019.6.16          |           py37_0         154 KB  defaults
    conda-4.7.5                |           py37_0         3.0 MB  defaults
    conda-build-3.18.5         |           py37_0         517 KB  defaults
    conda-package-handling-1.3.10|           py37_0         259 KB  defaults
    ------------------------------------------------------------
                                           Total:         3.9 MB

The following NEW packages will be INSTALLED:

  conda-package-han~ pkgs/main/linux-64::conda-package-handling-1.3.10-py37_0

The following packages will be UPDATED:

  certifi                                   2019.3.9-py37_0 --> 2019.6.16-py37_0
  conda                                       4.6.14-py37_0 --> 4.7.5-py37_0
  conda-build                                 3.17.8-py37_0 --> 3.18.5-py37_0


Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done
[root@chi9 build_scripts]# conda --version
conda 4.7.5
[root@chi9 build_scripts]# conda install conda=4.6
Collecting package metadata (current_repodata.json): done
Solving environment: failed
Collecting package metadata (repodata.json): done
Solving environment: done

# All requested packages already installed.
```

Changing `auto_update_conda`:

```
root@chi9 build_scripts]# CONDA_AUTO_UPDATE_CONDA=0 conda install conda=4.6
Collecting package metadata (current_repodata.json): done
Solving environment: failed
Collecting package metadata (repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: /opt/conda

  added / updated specs:
    - conda=4.6


The following packages will be DOWNGRADED:

  conda                                        4.7.5-py37_0 --> 4.6.14-py37_0


Proceed ([y]/n)? 
```

## Expected Behavior
Conda should allow the installation of a specific version when it is provided explicitly on the command line.


## Environment Information
<details open><summary><code>`conda info`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda info' -->

```
[root@chi9 build_scripts]# conda inf

CommandNotFoundError: No command 'conda inf'.
Did you mean 'conda info'?

[root@chi9 build_scripts]# conda info

     active environment : None
       user config file : /root/.condarc
 populated config files : /root/.condarc
          conda version : 4.7.5
    conda-build version : 3.18.5
         python version : 3.7.3.final.0
       virtual packages : 
       base environment : /opt/conda  (writable)
           channel URLs : https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
          package cache : /opt/conda/pkgs
                          /root/.conda/pkgs
       envs directories : /opt/conda/envs
                          /root/.conda/envs
               platform : linux-64
             user-agent : conda/4.7.5 requests/2.22.0 CPython/3.7.3 Linux/4.15.0-51-generic centos/6.9 glibc/2.12
                UID:GID : 0:0
             netrc file : None
           offline mode : False
```
</p></details>


<details open><summary><code>`conda config --show-sources`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda config --show-sources' -->

```
[root@chi9 build_scripts]# conda config --show-sources
==> /root/.condarc <==
add_pip_as_python_dependency: False
show_channel_urls: True
```
</p></details>


<details><summary><code>`conda list --show-channel-urls`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda list --show-channel-urls' -->

```
[root@chi9 build_scripts]# conda list --show-channel-urls
# packages in environment at /opt/conda:
#
# Name                    Version                   Build  Channel
anaconda-client           1.7.2                    py37_0    defaults
asn1crypto                0.24.0                   py37_0    defaults
attrs                     19.1.0                   py37_1    defaults
beautifulsoup4            4.7.1                    py37_1    defaults
binutils_impl_linux-64    2.31.1               h6176602_1    defaults
binutils_linux-64         2.31.1               h6176602_7    defaults
bzip2                     1.0.6                h14c3975_5    defaults
ca-certificates           2019.5.15                     0    defaults
certifi                   2019.6.16                py37_0    defaults
cffi                      1.12.3           py37h2e261b9_0    defaults
chardet                   3.0.4                    py37_1    defaults
clyent                    1.2.2                    py37_1    defaults
conda                     4.7.5                    py37_0    defaults
conda-build               3.18.5                   py37_0    defaults
conda-concourse-ci        0.1.0                    pypi_0    pypi
conda-package-handling    1.3.10                   py37_0    defaults
cryptography              2.7              py37h1ba5d50_0    defaults
curl                      7.64.1               hbc83047_0    defaults
decorator                 4.4.0                    py37_1    defaults
expat                     2.2.6                he6710b0_0    defaults
filelock                  3.0.10                   py37_0    defaults
gcc_impl_linux-64         7.3.0                habb00fd_1    defaults
gcc_linux-64              7.3.0                h553295d_7    defaults
git                       2.20.1          pl526hacde149_0    defaults
glob2                     0.6                      py37_1    defaults
gxx_impl_linux-64         7.3.0                hdf63c60_1    defaults
gxx_linux-64              7.3.0                h553295d_7    defaults
icu                       58.2                 h9c2bf20_1    defaults
idna                      2.8                      py37_0    defaults
ipython_genutils          0.2.0                    py37_0    defaults
jinja2                    2.10.1                   py37_0    defaults
jsonschema                3.0.1                    py37_0    defaults
jupyter_core              4.4.0                    py37_0    defaults
krb5                      1.16.1               h173b8e3_7    defaults
libarchive                3.3.3                h5d8350f_5    defaults
libcurl                   7.64.1               h20c2e04_0    defaults
libedit                   3.1.20181209         hc058e9b_0    defaults
libffi                    3.2.1                hd88cf55_4    defaults
libgcc-ng                 8.2.0                hdf63c60_1    defaults
liblief                   0.9.0                h7725739_2    defaults
libssh2                   1.8.2                h1ba5d50_0    defaults
libstdcxx-ng              8.2.0                hdf63c60_1    defaults
libxml2                   2.9.9                he19cac6_0    defaults
lz4-c                     1.8.1.2              h14c3975_0    defaults
lzo                       2.10                 h49e0be7_2    defaults
markupsafe                1.1.1            py37h7b6447c_0    defaults
nbformat                  4.4.0                    py37_0    defaults
ncurses                   6.1                  he6710b0_1    defaults
openssl                   1.1.1c               h7b6447c_1    defaults
patchelf                  0.9                  he6710b0_3    defaults
perl                      5.26.2               h14c3975_0    defaults
pip                       19.1.1                   py37_0    defaults
pkginfo                   1.5.0.1                  py37_0    defaults
psutil                    5.6.2            py37h7b6447c_0    defaults
py-lief                   0.9.0            py37h7725739_2    defaults
pycosat                   0.6.3            py37h14c3975_0    defaults
pycparser                 2.19                     py37_0    defaults
pyopenssl                 19.0.0                   py37_0    defaults
pyrsistent                0.14.11          py37h7b6447c_0    defaults
pysocks                   1.7.0                    py37_0    defaults
python                    3.7.3                h0371630_0    defaults
python-dateutil           2.8.0                    py37_0    defaults
python-libarchive-c       2.8                      py37_6    defaults
pytz                      2019.1                     py_0    defaults
pyyaml                    5.1              py37h7b6447c_0    defaults
readline                  7.0                  h7b6447c_5    defaults
requests                  2.22.0                   py37_0    defaults
ruamel_yaml               0.15.46          py37h14c3975_0    defaults
setuptools                41.0.1                   py37_0    defaults
six                       1.12.0                   py37_0    defaults
soupsieve                 1.8                      py37_0    defaults
sqlite                    3.28.0               h7b6447c_0    defaults
tk                        8.6.8                hbc83047_0    defaults
tqdm                      4.31.1                   py37_1    defaults
traitlets                 4.3.2                    py37_0    defaults
urllib3                   1.24.2                   py37_0    defaults
wheel                     0.33.4                   py37_0    defaults
xz                        5.2.4                h14c3975_4    defaults
yaml                      0.1.7                had09818_2    defaults
zlib                      1.2.11               h7b6447c_3    defaults
zstd                      1.3.7                h0b5b093_0    defaults
```
</p></details>



Discussion:
@ocefpaf I believe you mentioned this bug on the conda-forge gitter channel.  A workaround if you need to downgrade conda from 4.7 is to use `CONDA_AUTO_UPDATE_CONDA=0` or set the corresponding configuration value in .condarc.
Thanks! That worked nicely.
I just hit this problem as well (a "problem" because conda 4.7.5 seems several other bugs that required me to roll back). Another workaround (if you didn't start with 4.7.5) is to use `conda list --revisions` and `conda install --revision <rev>` to roll back to the environment just before you upgraded to 4.7.5.

Resolving patch:
diff --git a/conda/core/solve.py b/conda/core/solve.py
--- a/conda/core/solve.py
+++ b/conda/core/solve.py
@@ -556,12 +556,13 @@ def _add_specs(self, ssc):
 
         # As a business rule, we never want to update python beyond the current minor version,
         # unless that's requested explicitly by the user (which we actively discourage).
-        if (any(_.name == 'python' for _ in ssc.solution_precs)
-                and not any(s.name == 'python' for s in self.specs_to_add)):
+        py_in_prefix = any(_.name == 'python' for _ in ssc.solution_precs)
+        py_requested_explicitly = any(s.name == 'python' for s in self.specs_to_add)
+        if py_in_prefix and not py_requested_explicitly:
 
             python_prefix_rec = ssc.prefix_data.get('python')
-            if ('python' not in conflict_specs and
-                    ssc.update_modifier == UpdateModifier.FREEZE_INSTALLED):
+            freeze_installed = ssc.update_modifier == UpdateModifier.FREEZE_INSTALLED
+            if 'python' not in conflict_specs and freeze_installed:
                 ssc.specs_map['python'] = python_prefix_rec.to_match_spec()
             else:
                 # will our prefix record conflict with any explict spec?  If so, don't add
@@ -593,13 +594,14 @@ def _add_specs(self, ssc):
         if 'conda' in ssc.specs_map and paths_equal(self.prefix, context.conda_prefix):
             conda_prefix_rec = ssc.prefix_data.get('conda')
             if conda_prefix_rec:
+                version_req = ">=%s" % conda_prefix_rec.version
+                conda_requested_explicitly = any(s.name == 'conda' for s in self.specs_to_add)
                 conda_spec = ssc.specs_map['conda']
-
                 conda_in_specs_to_add_version = ssc.specs_map.get('conda', {}).get('version')
                 if not conda_in_specs_to_add_version:
-                    conda_spec = MatchSpec(conda_spec, version=">=%s" % conda_prefix_rec.version)
-                if context.auto_update_conda:
-                    conda_spec = MatchSpec('conda', target=None)
+                    conda_spec = MatchSpec(conda_spec, version=version_req)
+                if context.auto_update_conda and not conda_requested_explicitly:
+                    conda_spec = MatchSpec('conda', version=version_req, target=None)
                 ssc.specs_map['conda'] = conda_spec
 
         return ssc


Test patch:
diff --git a/tests/core/test_solve.py b/tests/core/test_solve.py
--- a/tests/core/test_solve.py
+++ b/tests/core/test_solve.py
@@ -903,6 +903,92 @@ def test_auto_update_conda():
         sys.prefix = saved_sys_prefix
 
 
+def test_explicit_conda_downgrade():
+    specs = MatchSpec("conda=1.5"),
+    with get_solver(specs) as solver:
+        final_state_1 = solver.solve_final_state()
+        # PrefixDag(final_state_1, specs).open_url()
+        print(convert_to_dist_str(final_state_1))
+        order = (
+            'channel-1::openssl-1.0.1c-0',
+            'channel-1::readline-6.2-0',
+            'channel-1::sqlite-3.7.13-0',
+            'channel-1::system-5.8-1',
+            'channel-1::tk-8.5.13-0',
+            'channel-1::yaml-0.1.4-0',
+            'channel-1::zlib-1.2.7-0',
+            'channel-1::python-2.7.5-0',
+            'channel-1::pyyaml-3.10-py27_0',
+            'channel-1::conda-1.5.2-py27_0',
+        )
+        assert convert_to_dist_str(final_state_1) == order
+
+    with env_vars({"CONDA_AUTO_UPDATE_CONDA": "yes"}, stack_callback=conda_tests_ctxt_mgmt_def_pol):
+        specs_to_add = MatchSpec("conda=1.3"),
+        with get_solver(specs_to_add, prefix_records=final_state_1, history_specs=specs) as solver:
+            final_state_2 = solver.solve_final_state()
+            # PrefixDag(final_state_2, specs).open_url()
+            print(convert_to_dist_str(final_state_2))
+            order = (
+                'channel-1::openssl-1.0.1c-0',
+                'channel-1::readline-6.2-0',
+                'channel-1::sqlite-3.7.13-0',
+                'channel-1::system-5.8-1',
+                'channel-1::tk-8.5.13-0',
+                'channel-1::yaml-0.1.4-0',
+                'channel-1::zlib-1.2.7-0',
+                'channel-1::python-2.7.5-0',
+                'channel-1::pyyaml-3.10-py27_0',
+                'channel-1::conda-1.3.5-py27_0',
+            )
+            assert convert_to_dist_str(final_state_2) == order
+
+    saved_sys_prefix = sys.prefix
+    try:
+        sys.prefix = TEST_PREFIX
+        with env_vars({"CONDA_AUTO_UPDATE_CONDA": "yes"}, stack_callback=conda_tests_ctxt_mgmt_def_pol):
+            specs_to_add = MatchSpec("conda=1.3"),
+            with get_solver(specs_to_add, prefix_records=final_state_1, history_specs=specs) as solver:
+                final_state_2 = solver.solve_final_state()
+                # PrefixDag(final_state_2, specs).open_url()
+                print(convert_to_dist_str(final_state_2))
+                order = (
+                    'channel-1::openssl-1.0.1c-0',
+                    'channel-1::readline-6.2-0',
+                    'channel-1::sqlite-3.7.13-0',
+                    'channel-1::system-5.8-1',
+                    'channel-1::tk-8.5.13-0',
+                    'channel-1::yaml-0.1.4-0',
+                    'channel-1::zlib-1.2.7-0',
+                    'channel-1::python-2.7.5-0',
+                    'channel-1::pyyaml-3.10-py27_0',
+                    'channel-1::conda-1.3.5-py27_0',
+                )
+                assert convert_to_dist_str(final_state_2) == order
+
+        with env_vars({"CONDA_AUTO_UPDATE_CONDA": "no"}, stack_callback=conda_tests_ctxt_mgmt_def_pol):
+            specs_to_add = MatchSpec("conda=1.3"),
+            with get_solver(specs_to_add, prefix_records=final_state_1, history_specs=specs) as solver:
+                final_state_2 = solver.solve_final_state()
+                # PrefixDag(final_state_2, specs).open_url()
+                print(convert_to_dist_str(final_state_2))
+                order = (
+                    'channel-1::openssl-1.0.1c-0',
+                    'channel-1::readline-6.2-0',
+                    'channel-1::sqlite-3.7.13-0',
+                    'channel-1::system-5.8-1',
+                    'channel-1::tk-8.5.13-0',
+                    'channel-1::yaml-0.1.4-0',
+                    'channel-1::zlib-1.2.7-0',
+                    'channel-1::python-2.7.5-0',
+                    'channel-1::pyyaml-3.10-py27_0',
+                    'channel-1::conda-1.3.5-py27_0',
+                )
+                assert convert_to_dist_str(final_state_2) == order
+    finally:
+        sys.prefix = saved_sys_prefix
+
+
 def test_aggressive_update_packages():
     def solve(prev_state, specs_to_add, order):
         final_state_1, specs = prev_state