Repository: pypa/pipenv

Issue:
Better error messages (instead of exceptions) when parsing malformed [[source]] section
I have a problem with installing dev dependency. In my project I have this Pipfile : 

```
url = "https://pypi.org/simple"
[[source]]
verify_ssl = true
name = "pypi"

[packages]
argparse = "*"
argcomplete = "*"
prettytable = "*"

[dev-packages]
pyinstaller = "*"

[scripts]
build = "pyinstaller main.spec"

[requires]
python_version = "3.6"
```

When I install dev dependency I got an KeyError (see below). 
I tried to create a new folder with same dev dependency and it worked.
I tried to remove Virtualenv, remove pipenv cache folder but I got same error.

```
Python 3.6.3
pipenv, version 2018.05.18
```

------------

##### Expected result

Dev dependency install without error

##### Actual result

`$ pipenv install --dev`
```
Installing pyinstaller…
Traceback (most recent call last):
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/bin/pipenv", line 11, in <module>
    load_entry_point('pipenv==2018.5.18', 'console_scripts', 'pipenv')()
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/vendor/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/cli.py", line 402, in install
    selective_upgrade=selective_upgrade,
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/core.py", line 1978, in do_install
    extra_indexes=extra_indexes,
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/core.py", line 1494, in pip_install
    ' '.join(prepare_pip_source_args(sources)),
  File "/usr/local/Cellar/pipenv/2018.5.18/libexec/lib/python3.6/site-packages/pipenv/utils.py", line 285, in prepare_pip_source_args
    pip_args.extend(['-i', sources[0]['url']])
KeyError: 'url'
```
##### Steps to replicate

`pipenv install --dev`


Discussion:
Um, because you put the `url` lin in the wrong place? It should be under `[[source]]`, not above. We can probably emit better error messages for this, but you need to fix your Pipfile.
So simple. Thanks, it works
@uranusjr Is anyone working on this? If not, mind if I give it a try?

Resolving patch:
diff --git a/pipenv/utils.py b/pipenv/utils.py
--- a/pipenv/utils.py
+++ b/pipenv/utils.py
@@ -31,6 +31,7 @@
 import parse
 
 from . import environments
+from .exceptions import PipenvUsageError
 from .pep508checker import lookup
 
 
@@ -203,20 +204,26 @@ def prepare_pip_source_args(sources, pip_args=None):
         pip_args = []
     if sources:
         # Add the source to notpip.
-        pip_args.extend(["-i", sources[0]["url"]])
+        package_url = sources[0].get("url")
+        if not package_url:
+            raise PipenvUsageError("[[source]] section does not contain a URL.")
+        pip_args.extend(["-i", package_url])
         # Trust the host if it's not verified.
         if not sources[0].get("verify_ssl", True):
             pip_args.extend(
-                ["--trusted-host", urllib3_util.parse_url(sources[0]["url"]).host]
+                ["--trusted-host", urllib3_util.parse_url(package_url).host]
             )
         # Add additional sources as extra indexes.
         if len(sources) > 1:
             for source in sources[1:]:
-                pip_args.extend(["--extra-index-url", source["url"]])
+                url = source.get("url")
+                if not url:  # not harmless, just don't continue
+                    continue
+                pip_args.extend(["--extra-index-url", url])
                 # Trust the host if it's not verified.
                 if not source.get("verify_ssl", True):
                     pip_args.extend(
-                        ["--trusted-host", urllib3_util.parse_url(source["url"]).host]
+                        ["--trusted-host", urllib3_util.parse_url(url).host]
                     )
     return pip_args
 


Test patch:
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -8,6 +8,7 @@
 
 import pipenv.utils
 import pythonfinder.utils
+from pipenv.exceptions import PipenvUsageError
 
 
 # Pipfile format <-> requirements.txt format.
@@ -374,6 +375,11 @@ def test_prepare_pip_source_args(self, sources, expected_args):
             == expected_args
         )
 
+    def test_invalid_prepare_pip_source_args(self):
+        sources = [{}]
+        with pytest.raises(PipenvUsageError):
+            pipenv.utils.prepare_pip_source_args(sources, pip_args=None)
+
     @pytest.mark.utils
     def test_parse_python_version(self):
         ver = pipenv.utils.parse_python_version("Python 3.6.5\n")