# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

import re
import socket
import sys
from getpass import getpass
from logging import getLogger
from os.path import abspath, expanduser

try:
    # Python 3
    from urllib.parse import (quote, quote_plus, unquote, unquote_plus,  # NOQA
                              urlunparse as stdlib_urlparse, urljoin)  # NOQA
    from urllib.request import pathname2url  # NOQA
except ImportError:
    # Python 2
    from urllib import quote, quote_plus, unquote, unquote_plus, pathname2url  # NOQA
    from urlparse import urlunparse as stdlib_urlparse, urljoin  # NOQA

from requests.packages.urllib3.exceptions import LocationParseError
from requests.packages.urllib3.util.url import Url, parse_url

from .._vendor.auxlib.decorators import memoize

log = getLogger(__name__)


on_win = bool(sys.platform == "win32")


@memoize
def path_to_url(path):
    path = abspath(expanduser(path))
    url = urljoin('file:', pathname2url(path))
    log.debug("%s converted to %s", path, url)
    return url


def url_to_path(url):  # NOQA
    """Convert a file:// URL to a path."""
    assert url.startswith('file:'), "You can only turn file: urls into filenames (not %r)" % url
    path = url[len('file:'):].lstrip('/')
    path = unquote(path)
    if re.match('^([a-z])[:|]', path, re.I):
        path = path[0] + ':' + path[2:]
    elif not path.startswith(r'\\'):
        # if not a Windows UNC path
        path = '/' + path
    return path


@memoize
def urlparse(url):
    if on_win and url.startswith('file:'):
        url.replace('\\', '/')
    return parse_url(url)


def url_to_s3_info(url):
    """
    Convert a S3 url to a tuple of bucket and key
    """
    parsed_url = parse_url(url)
    assert parsed_url.scheme == 's3', "You can only use s3: urls (not %r)" % url
    bucket, key = parsed_url.host, parsed_url.path
    return bucket, key


def is_url(url):
    try:
        p = urlparse(url)
        return p.netloc is not None or p.scheme == "file"
    except LocationParseError:
        log.debug("Could not parse url ({0}).".format(url))
        return False


def is_ipv4_address(string_ip):
    """
    Examples:
        >>> [is_ipv4_address(ip) for ip in ('8.8.8.8', '192.168.10.10', '255.255.255.255')]
        [True, True, True]
        >>> [is_ipv4_address(ip) for ip in ('8.8.8', '192.168.10.10.20', '256.255.255.255', '::1')]
        [False, False, False, False]
    """
    try:
        socket.inet_aton(string_ip)
    except socket.error:
        return False
    return string_ip.count('.') == 3


def is_ipv6_address(string_ip):
    """
    Examples:
        >>> [is_ipv6_address(ip) for ip in ('::1', '2001:db8:85a3::370:7334', '1234:'*7+'1234')]
        [True, True, True]
        >>> [is_ipv6_address(ip) for ip in ('192.168.10.10', '1234:'*8+'1234')]
        [False, False]
    """
    try:
        socket.inet_pton(socket.AF_INET6, string_ip)
    except socket.error:
        return False
    return True


def is_ip_address(string_ip):
    """
    Examples:
        >>> is_ip_address('192.168.10.10')
        True
        >>> is_ip_address('::1')
        True
        >>> is_ip_address('www.google.com')
        False
    """
    return is_ipv4_address(string_ip) or is_ipv6_address(string_ip)


def join(*args):
    start = '/' if not args[0] or args[0].startswith('/') else ''
    return start + '/'.join(y for y in (x.strip('/') for x in args if x) if y)


join_url = join


def has_scheme(value):
    return re.match(r'[a-z][a-z0-9]{0,11}://', value)


def strip_scheme(url):
    return url.split('://', 1)[-1]


def mask_anaconda_token(url):
    _, token = split_anaconda_token(url)
    return url.replace(token, "<TOKEN>", 1) if token else url


def split_anaconda_token(url):
    """
    Examples:
        >>> split_anaconda_token("https://1.2.3.4/t/tk-123-456/path")
        (u'https://1.2.3.4/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/t//path")
        (u'https://1.2.3.4/path', u'')
        >>> split_anaconda_token("https://some.domain/api/t/tk-123-456/path")
        (u'https://some.domain/api/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/conda/t/tk-123-456/path")
        (u'https://1.2.3.4/conda/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/path")
        (u'https://1.2.3.4/path', None)
        >>> split_anaconda_token("https://10.2.3.4:8080/conda/t/tk-123-45")
        (u'https://10.2.3.4:8080/conda', u'tk-123-45')
    """
    _token_match = re.search(r'/t/([a-zA-Z0-9-]*)', url)
    token = _token_match.groups()[0] if _token_match else None
    cleaned_url = url.replace('/t/' + token, '', 1) if token is not None else url
    return cleaned_url.rstrip('/'), token


def split_platform(url):
    """

    Examples:
        >>> split_platform("https://1.2.3.4/t/tk-123/osx-64/path")
        (u'https://1.2.3.4/t/tk-123/path', u'osx-64')

    """
    from conda.base.constants import PLATFORM_DIRECTORIES
    _platform_match_regex = r'/(%s)/?' % r'|'.join(r'%s' % d for d in PLATFORM_DIRECTORIES)
    _platform_match = re.search(_platform_match_regex, url, re.IGNORECASE)
    platform = _platform_match.groups()[0] if _platform_match else None
    cleaned_url = url.replace('/' + platform, '', 1) if platform is not None else url
    return cleaned_url.rstrip('/'), platform


def split_package_filename(url):
    cleaned_url, package_filename = (url.rsplit('/', 1) if url.endswith(('.tar.bz2', '.json'))
                                     else (url, None))
    return cleaned_url, package_filename


def split_scheme_auth_token(url):
    if not url:
        return None, None, None, None
    cleaned_url, token = split_anaconda_token(url)
    url_parts = urlparse(cleaned_url)
    remainder_url = Url(host=url_parts.host, port=url_parts.port, path=url_parts.path,
                        query=url_parts.query).url
    return remainder_url, url_parts.scheme, url_parts.auth, token


def split_conda_url_easy_parts(url):
    # scheme, auth, token, platform, package_filename, host, port, path, query
    cleaned_url, token = split_anaconda_token(url)
    cleaned_url, platform = split_platform(cleaned_url)
    cleaned_url, package_filename = split_package_filename(cleaned_url)

    # TODO: split out namespace using regex

    url_parts = urlparse(cleaned_url)

    return (url_parts.scheme, url_parts.auth, token, platform, package_filename, url_parts.host,
            url_parts.port, url_parts.path, url_parts.query)


def is_windows_path(value):
    return re.match(r'[a-z]:[/\\]', value, re.IGNORECASE)


@memoize
def get_proxy_username_and_pass(scheme):
    username = input("\n%s proxy username: " % scheme)
    passwd = getpass("Password:")
    return username, passwd


def add_username_and_password(url, username, password):
    url_parts = parse_url(url)._asdict()
    url_parts['auth'] = username + ':' + quote(password, '')
    return Url(**url_parts).url


def maybe_add_auth(url, auth, force=False):
    """add auth if the url doesn't currently have it"""
    if not auth:
        return url
    url_parts = urlparse(url)._asdict()
    if url_parts['auth'] and not force:
        return url
    url_parts['auth'] = auth
    return Url(**url_parts).url


if __name__ == "__main__":
    import doctest
    doctest.testmod()

conda/common/url.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

import re
import socket
import sys
from getpass import getpass
from logging import getLogger
from os.path import abspath, expanduser

try:
    # Python 3
    from urllib.parse import (quote, quote_plus, unquote, unquote_plus,  # NOQA
                              urlunparse as stdlib_urlparse, urljoin)  # NOQA
    from urllib.request import pathname2url  # NOQA
except ImportError:
    # Python 2
    from urllib import quote, quote_plus, unquote, unquote_plus, pathname2url  # NOQA
    from urlparse import urlunparse as stdlib_urlparse, urljoin  # NOQA

from requests.packages.urllib3.exceptions import LocationParseError
from requests.packages.urllib3.util.url import Url, parse_url

from .._vendor.auxlib.decorators import memoize

log = getLogger(__name__)


on_win = bool(sys.platform == "win32")


@memoize
def path_to_url(path):
    path = abspath(expanduser(path))
    url = urljoin('file:', pathname2url(path))
    log.debug("%s converted to %s", path, url)
    return url


def url_to_path(url):  # NOQA
    """Convert a file:// URL to a path."""
    assert url.startswith('file:'), "You can only turn file: urls into filenames (not %r)" % url
    path = url[len('file:'):].lstrip('/')
    path = unquote(path)
    if re.match('^([a-z])[:|]', path, re.I):
        path = path[0] + ':' + path[2:]
    elif not path.startswith(r'\\'):
        # if not a Windows UNC path
        path = '/' + path
    return path


@memoize
def urlparse(url):
    if on_win and url.startswith('file:'):
        url.replace('\\', '/')
    return parse_url(url)


def url_to_s3_info(url):
    """
    Convert a S3 url to a tuple of bucket and key
    """
    parsed_url = parse_url(url)
    assert parsed_url.scheme == 's3', "You can only use s3: urls (not %r)" % url
    bucket, key = parsed_url.host, parsed_url.path
    return bucket, key


def is_url(url):
    if not url:
        return False
    try:
        p = urlparse(url)
        return p.netloc is not None or p.scheme == "file"
    except LocationParseError:
        log.debug("Could not parse url ({0}).".format(url))
        return False


def is_ipv4_address(string_ip):
    """
    Examples:
        >>> [is_ipv4_address(ip) for ip in ('8.8.8.8', '192.168.10.10', '255.255.255.255')]
        [True, True, True]
        >>> [is_ipv4_address(ip) for ip in ('8.8.8', '192.168.10.10.20', '256.255.255.255', '::1')]
        [False, False, False, False]
    """
    try:
        socket.inet_aton(string_ip)
    except socket.error:
        return False
    return string_ip.count('.') == 3


def is_ipv6_address(string_ip):
    """
    Examples:
        >>> [is_ipv6_address(ip) for ip in ('::1', '2001:db8:85a3::370:7334', '1234:'*7+'1234')]
        [True, True, True]
        >>> [is_ipv6_address(ip) for ip in ('192.168.10.10', '1234:'*8+'1234')]
        [False, False]
    """
    try:
        socket.inet_pton(socket.AF_INET6, string_ip)
    except socket.error:
        return False
    return True


def is_ip_address(string_ip):
    """
    Examples:
        >>> is_ip_address('192.168.10.10')
        True
        >>> is_ip_address('::1')
        True
        >>> is_ip_address('www.google.com')
        False
    """
    return is_ipv4_address(string_ip) or is_ipv6_address(string_ip)


def join(*args):
    start = '/' if not args[0] or args[0].startswith('/') else ''
    return start + '/'.join(y for y in (x.strip('/') for x in args if x) if y)


join_url = join


def has_scheme(value):
    return re.match(r'[a-z][a-z0-9]{0,11}://', value)


def strip_scheme(url):
    return url.split('://', 1)[-1]


def mask_anaconda_token(url):
    _, token = split_anaconda_token(url)
    return url.replace(token, "<TOKEN>", 1) if token else url


def split_anaconda_token(url):
    """
    Examples:
        >>> split_anaconda_token("https://1.2.3.4/t/tk-123-456/path")
        (u'https://1.2.3.4/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/t//path")
        (u'https://1.2.3.4/path', u'')
        >>> split_anaconda_token("https://some.domain/api/t/tk-123-456/path")
        (u'https://some.domain/api/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/conda/t/tk-123-456/path")
        (u'https://1.2.3.4/conda/path', u'tk-123-456')
        >>> split_anaconda_token("https://1.2.3.4/path")
        (u'https://1.2.3.4/path', None)
        >>> split_anaconda_token("https://10.2.3.4:8080/conda/t/tk-123-45")
        (u'https://10.2.3.4:8080/conda', u'tk-123-45')
    """
    _token_match = re.search(r'/t/([a-zA-Z0-9-]*)', url)
    token = _token_match.groups()[0] if _token_match else None
    cleaned_url = url.replace('/t/' + token, '', 1) if token is not None else url
    return cleaned_url.rstrip('/'), token


def split_platform(url):
    """

    Examples:
        >>> split_platform("https://1.2.3.4/t/tk-123/osx-64/path")
        (u'https://1.2.3.4/t/tk-123/path', u'osx-64')

    """
    from conda.base.constants import PLATFORM_DIRECTORIES
    _platform_match_regex = r'/(%s)/?' % r'|'.join(r'%s' % d for d in PLATFORM_DIRECTORIES)
    _platform_match = re.search(_platform_match_regex, url, re.IGNORECASE)
    platform = _platform_match.groups()[0] if _platform_match else None
    cleaned_url = url.replace('/' + platform, '', 1) if platform is not None else url
    return cleaned_url.rstrip('/'), platform


def split_package_filename(url):
    cleaned_url, package_filename = (url.rsplit('/', 1) if url.endswith(('.tar.bz2', '.json'))
                                     else (url, None))
    return cleaned_url, package_filename


def split_scheme_auth_token(url):
    if not url:
        return None, None, None, None
    cleaned_url, token = split_anaconda_token(url)
    url_parts = urlparse(cleaned_url)
    remainder_url = Url(host=url_parts.host, port=url_parts.port, path=url_parts.path,
                        query=url_parts.query).url
    return remainder_url, url_parts.scheme, url_parts.auth, token


def split_conda_url_easy_parts(url):
    # scheme, auth, token, platform, package_filename, host, port, path, query
    cleaned_url, token = split_anaconda_token(url)
    cleaned_url, platform = split_platform(cleaned_url)
    cleaned_url, package_filename = split_package_filename(cleaned_url)

    # TODO: split out namespace using regex

    url_parts = urlparse(cleaned_url)

    return (url_parts.scheme, url_parts.auth, token, platform, package_filename, url_parts.host,
            url_parts.port, url_parts.path, url_parts.query)


def is_windows_path(value):
    return re.match(r'[a-z]:[/\\]', value, re.IGNORECASE)


@memoize
def get_proxy_username_and_pass(scheme):
    username = input("\n%s proxy username: " % scheme)
    passwd = getpass("Password:")
    return username, passwd


def add_username_and_password(url, username, password):
    url_parts = parse_url(url)._asdict()
    url_parts['auth'] = username + ':' + quote(password, '')
    return Url(**url_parts).url


def maybe_add_auth(url, auth, force=False):
    """add auth if the url doesn't currently have it"""
    if not auth:
        return url
    url_parts = urlparse(url)._asdict()
    if url_parts['auth'] and not force:
        return url
    url_parts['auth'] = auth
    return Url(**url_parts).url


if __name__ == "__main__":
    import doctest
    doctest.testmod()

conda/common/url.py
diff --git a/conda/common/url.py b/conda/common/url.py
index a25525815e9..4cda8593d25 100644
--- a/conda/common/url.py
+++ b/conda/common/url.py
@@ -68,6 +68,8 @@ def url_to_s3_info(url):
 
 
 def is_url(url):
+    if not url:
+        return False
     try:
         p = urlparse(url)
         return p.netloc is not None or p.scheme == "file"

Regression: cannot install from explicit conda package filenames
This command used to work, but now it gives the following error/traceback:

Example: `conda install bzip2-1.0.6-vc14_3.tar.bz2 --dry-run`

```
An unexpected error has occurred.
Please consider posting the following information to the
conda GitHub issue tracker at:

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



Current conda install:

               platform : win-64
          conda version : 4.2.12
       conda is private : False
      conda-env version : 4.2.12
    conda-build version : 2.0.7
         python version : 3.5.2.final.0
       requests version : 2.10.0
       root environment : C:\Miniconda3  (writable)
    default environment : C:\Miniconda3\envs\test_conda
       envs directories : C:\Miniconda3\envs
          package cache : C:\Miniconda3\pkgs
           channel URLs : https://repo.continuum.io/pkgs/free/win-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/pro/win-64
                          https://repo.continuum.io/pkgs/pro/noarch
                          https://repo.continuum.io/pkgs/msys2/win-64
                          https://repo.continuum.io/pkgs/msys2/noarch
            config file : None
           offline mode : False



`$ C:\Miniconda3\Scripts\conda-script.py install bzip2-1.0.6-vc14_3.tar.bz2 --dry-run`




    Traceback (most recent call last):
      File "C:\Miniconda3\lib\site-packages\conda\exceptions.py", line 479, in conda_exception_handler
        return_value = func(*args, **kwargs)
      File "C:\Miniconda3\lib\site-packages\conda\cli\main.py", line 145, in _main
        exit_code = args.func(args, p)
      File "C:\Miniconda3\lib\site-packages\conda\cli\main_install.py", line 80, in execute
        install(args, parser, 'install')
      File "C:\Miniconda3\lib\site-packages\conda\cli\install.py", line 209, in install
        explicit(args.packages, prefix, verbose=not context.quiet)
      File "C:\Miniconda3\lib\site-packages\conda\misc.py", line 66, in explicit
        if not is_url(url_p):
      File "C:\Miniconda3\lib\site-packages\conda\common\url.py", line 72, in is_url
        p = urlparse(url)
      File "C:\Miniconda3\lib\site-packages\conda\_vendor\auxlib\decorators.py", line 56, in _memoized_func
        result = func(*args, **kwargs)
      File "C:\Miniconda3\lib\site-packages\conda\common\url.py", line 55, in urlparse
        if on_win and url.startswith('file:'):
    AttributeError: 'NoneType' object has no attribute 'startswith'
```
