Repository: conda/conda

Issue:
conda install from tarball error?
Running into this issue when trying to install directly from a tarball.

```
Traceback (most recent call last):
  File "/usr/local/bin/conda2", line 6, in <module>
    sys.exit(main())
An unexpected error has occurred, please consider sending the
following traceback to the conda GitHub issue tracker at:

    https://github.com/conda/conda/issues

Include the output of the command 'conda info' in your report.


  File "/opt/conda2/lib/python2.7/site-packages/conda/cli/main.py", line 120, in main
    exit_code = args_func(args, p)
  File "/opt/conda2/lib/python2.7/site-packages/conda/cli/main.py", line 130, in args_func
    exit_code = args.func(args, p)
  File "/opt/conda2/lib/python2.7/site-packages/conda/cli/main_install.py", line 69, in execute
    install(args, parser, 'install')
  File "/opt/conda2/lib/python2.7/site-packages/conda/cli/install.py", line 196, in install
    explicit(args.packages, prefix, verbose=not args.quiet)
  File "/opt/conda2/lib/python2.7/site-packages/conda/misc.py", line 79, in explicit
    is_file = schannel.startswith('file:') and schannel.endswith('/')
UnboundLocalError: local variable 'schannel' referenced before assignment
```



Discussion:
`conda info`?

This is in a docker image.

```
$ conda info
Current conda install:

             platform : linux-64
        conda version : 4.1.4
    conda-env version : 2.5.1
  conda-build version : 1.20.0
       python version : 2.7.11.final.0
     requests version : 2.9.2
     root environment : /opt/conda2  (writable)
  default environment : /opt/conda2
     envs directories : /opt/conda2/envs
        package cache : /opt/conda2/pkgs
         channel URLs : https://conda.anaconda.org/nanshe/linux-64/
                        https://conda.anaconda.org/nanshe/noarch/
                        https://conda.anaconda.org/conda-forge/linux-64/
                        https://conda.anaconda.org/conda-forge/noarch/
                        https://repo.continuum.io/pkgs/free/linux-64/
                        https://repo.continuum.io/pkgs/free/noarch/
                        https://repo.continuum.io/pkgs/pro/linux-64/
                        https://repo.continuum.io/pkgs/pro/noarch/
          config file : /root/.condarc
         offline mode : False
    is foreign system : False
```

I'm having trouble reproducing this.

I know this should be obvious but can you give us the exact command line?

@jakirkham More details here would be helpful, including the specific package you tried to install by tarball. I'm genuinely having difficulty reproducing.

So, this is coming out of an open sourced Docker image. Though I really should come up with a simpler example. I started to and then other stuff came up. I'll give it another try.

Until I do here is the [Dockerfile](https://github.com/nanshe-org/docker_nanshe/blob/6fa60ad6f221731c17bf2277f5744f8b781095db/Dockerfile). Sorry it is so ugly. I've tried my best to make it readable given the constraints. The line that cause it to fail is this [one](https://github.com/nanshe-org/docker_nanshe/blob/6fa60ad6f221731c17bf2277f5744f8b781095db/Dockerfile#L29).

Basically, what happens is we install everything in the `root` environment of two different `conda`s. One for Python 2 and the other for Python 3. We then remove one package `nanshe` and download the source code matching that version so as to have the test suite. We then remove the package and run the test suite. Once complete we try to reinstall the package from a file which fails.

I was able to reproduce the problem---it is specifically limited to installing tarballs _in the package cache_. As a short-term fix you can copy the package out of the cache and then reinstall, I think. #2907 is the same issue. I'm working on a fix now. cc: @kalefranz 


Resolving patch:
diff --git a/conda/install.py b/conda/install.py
--- a/conda/install.py
+++ b/conda/install.py
@@ -41,7 +41,6 @@
 import tarfile
 import tempfile
 import time
-import tempfile
 import traceback
 from os.path import (abspath, basename, dirname, isdir, isfile, islink,
                      join, normpath)
diff --git a/conda/misc.py b/conda/misc.py
--- a/conda/misc.py
+++ b/conda/misc.py
@@ -69,14 +69,18 @@ def explicit(specs, prefix, verbose=False, force_extract=True, fetch_args=None,
         prefix = pkg_path = dir_path = None
         if url.startswith('file://'):
             prefix = cached_url(url)
+            if prefix is not None:
+                schannel = 'defaults' if prefix == '' else prefix[:-2]
+                is_file = False
 
         # If not, determine the channel name from the URL
         if prefix is None:
             channel, schannel = url_channel(url)
+            is_file = schannel.startswith('file:') and schannel.endswith('/')
             prefix = '' if schannel == 'defaults' else schannel + '::'
+
         fn = prefix + fn
         dist = fn[:-8]
-        is_file = schannel.startswith('file:') and schannel.endswith('/')
         # Add explicit file to index so we'll see it later
         if is_file:
             index[fn] = {'fn': dist2filename(fn), 'url': url, 'md5': None}


Test patch:
diff --git a/tests/test_create.py b/tests/test_create.py
--- a/tests/test_create.py
+++ b/tests/test_create.py
@@ -260,11 +260,32 @@ def test_tarball_install_and_bad_metadata(self):
             assert not package_is_installed(prefix, 'flask-0.10.1')
             assert_package_is_installed(prefix, 'python')
 
-            # Regression test for 2812
-            # install from local channel
             from conda.config import pkgs_dirs
             flask_fname = flask_data['fn']
             tar_old_path = join(pkgs_dirs[0], flask_fname)
+
+            # regression test for #2886 (part 1 of 2)
+            # install tarball from package cache, default channel
+            run_command(Commands.INSTALL, prefix, tar_old_path)
+            assert_package_is_installed(prefix, 'flask-0.')
+
+            # regression test for #2626
+            # install tarball with full path, outside channel
+            tar_new_path = join(prefix, flask_fname)
+            copyfile(tar_old_path, tar_new_path)
+            run_command(Commands.INSTALL, prefix, tar_new_path)
+            assert_package_is_installed(prefix, 'flask-0')
+
+            # regression test for #2626
+            # install tarball with relative path, outside channel
+            run_command(Commands.REMOVE, prefix, 'flask')
+            assert not package_is_installed(prefix, 'flask-0.10.1')
+            tar_new_path = relpath(tar_new_path)
+            run_command(Commands.INSTALL, prefix, tar_new_path)
+            assert_package_is_installed(prefix, 'flask-0.')
+
+            # Regression test for 2812
+            # install from local channel
             for field in ('url', 'channel', 'schannel'):
                 del flask_data[field]
             repodata = {'info': {}, 'packages':{flask_fname: flask_data}}
@@ -279,21 +300,12 @@ def test_tarball_install_and_bad_metadata(self):
                 run_command(Commands.INSTALL, prefix, '-c', channel, 'flask')
                 assert_package_is_installed(prefix, channel + '::' + 'flask-')
 
-            # regression test for #2626
-            # install tarball with full path
-            tar_new_path = join(prefix, flask_fname)
-            copyfile(tar_old_path, tar_new_path)
-            run_command(Commands.INSTALL, prefix, tar_new_path)
-            assert_package_is_installed(prefix, 'flask-0')
-
+            # regression test for #2886 (part 2 of 2)
+            # install tarball from package cache, local channel
             run_command(Commands.REMOVE, prefix, 'flask')
             assert not package_is_installed(prefix, 'flask-0')
-
-            # regression test for #2626
-            # install tarball with relative path
-            tar_new_path = relpath(tar_new_path)
-            run_command(Commands.INSTALL, prefix, tar_new_path)
-            assert_package_is_installed(prefix, 'flask-0.')
+            run_command(Commands.INSTALL, prefix, tar_old_path)
+            assert_package_is_installed(prefix, channel + '::' + 'flask-')
 
             # regression test for #2599
             linked_data_.clear()