Repository: jazzband/pip-tools

Issue:
pip-compile fails if output file already exists
Given a requirements.in file which consists only of the line:
```
-e ..
```
(that is, pull in requirements from the setup.py file one folder up from my requirements.in file)

The first pip-compile run works fine. The second one throws an error. If I delete the file that was generated by the first run, it will work again. There are no permissions issues (tried it with full perms on the file to check).

##### Environment Versions

1. os: OSX 10.11.6
1. python version: 2.7
1. pip version: 9.0.1
1. pip-tools version: 1.8.2

##### Steps to replicate

1. pip-compile -v -o requirements.txt
**SUCCESS**
2. pip-compile -v -o requirements.txt 
**FAILURE**

##### Expected result
An updated (or identical) requirements.txt file


##### Actual result
```python
Traceback (most recent call last):
  File "/Users/<username>/.virtualenvs/<virtualenv>/bin/pip-compile", line 11, in <module>
    sys.exit(cli())
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/piptools/scripts/compile.py", line 131, in cli
    key = key_from_req(ireq.req)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/piptools/utils.py", line 41, in key_from_req
    key = req.name
AttributeError: 'NoneType' object has no attribute 'name'
```


Discussion:
This issue should be fixed in the (unreleased) 1.9.0. Can you check that your use case is covered on the master branch? Thanks.

see #477
@davidovich 

My use case appears to be broken entirely in the master branch (i.e. it does not work even if the output file does not exist). 

I ran the same command on the same .in file. 

Here is the version, according to pip-compile --version:
```
pip-compile, version 1.8.3.dev23-ng62d85ea
```

Here is my output:
```python
⇒  pip-compile -v requirements.in -o requirements.txt
Using indexes:
  https://pypi.python.org/simple
Traceback (most recent call last):
  File "/Users/<username>/.virtualenvs/<virtualenv>/bin/pip-compile", line 9, in <module>
    load_entry_point('pip-tools==1.8.3.dev23-ng62d85ea', 'console_scripts', 'pip-compile')()
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/<username>/.virtualenvs/<virtualenv>/lib/python2.7/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/Users/<username>/workspace/Local/cloned/pip-tools/piptools/scripts/compile.py", line 183, in cli
    primary_packages = {key_from_req(ireq.req) for ireq in constraints if not ireq.constraint}
  File "/Users/<username>/workspace/Local/cloned/pip-tools/piptools/scripts/compile.py", line 183, in <setcomp>
    primary_packages = {key_from_req(ireq.req) for ireq in constraints if not ireq.constraint}
  File "/Users/<username>/workspace/Local/cloned/pip-tools/piptools/utils.py", line 44, in key_from_req
    key = req.name
AttributeError: 'NoneType' object has no attribute 'name'
```

...revising this comment. It looks like this was broken in 1.8.0 the same way it is broken in 1.8.2.

It was broken in a slightly different way in 1.8.1.

It worked in 1.7.0
OK, this was introduced by 5bf5b97db36f7ade80cbce40b4de2175886f7100

Will fix and add a test.

Resolving patch:
diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py
--- a/piptools/scripts/compile.py
+++ b/piptools/scripts/compile.py
@@ -179,9 +179,6 @@ def cli(verbose, dry_run, pre, rebuild, find_links, index_url, extra_index_url,
     # Check the given base set of constraints first
     Resolver.check_constraints(constraints)
 
-    # The requirement objects are modified in-place so we need to save off the list of primary packages first
-    primary_packages = {key_from_req(ireq.req) for ireq in constraints if not ireq.constraint}
-
     try:
         resolver = Resolver(constraints, repository, prereleases=pre,
                             clear_caches=rebuild, allow_unsafe=allow_unsafe)
@@ -236,7 +233,7 @@ def cli(verbose, dry_run, pre, rebuild, find_links, index_url, extra_index_url,
                           format_control=repository.finder.format_control)
     writer.write(results=results,
                  reverse_dependencies=reverse_dependencies,
-                 primary_packages=primary_packages,
+                 primary_packages={key_from_req(ireq.req) for ireq in constraints if not ireq.constraint},
                  markers={key_from_req(ireq.req): ireq.markers
                           for ireq in constraints if ireq.markers},
                  hashes=hashes)


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -161,7 +161,7 @@ def test_realistic_complex_sub_dependencies(tmpdir):
         assert out.exit_code == 0
 
 
-def invoke(command):
+def _invoke(command):
     """Invoke sub-process."""
     try:
         output = subprocess.check_output(
@@ -179,7 +179,7 @@ def invoke(command):
 def test_run_as_module_compile(tmpdir):
     """piptools can be run as ``python -m piptools ...``."""
 
-    status, output = invoke([
+    status, output = _invoke([
         sys.executable, '-m', 'piptools', 'compile', '--help',
     ])
 
@@ -193,7 +193,7 @@ def test_run_as_module_compile(tmpdir):
 def test_run_as_module_sync():
     """piptools can be run as ``python -m piptools ...``."""
 
-    status, output = invoke([
+    status, output = _invoke([
         sys.executable, '-m', 'piptools', 'sync', '--help',
     ])
 
@@ -202,3 +202,20 @@ def test_run_as_module_sync():
     assert output.startswith('Usage:')
     assert 'Synchronize virtual environment with' in output
     assert status == 0
+
+
+def test_editable_package(tmpdir):
+    """ piptools can compile an editable """
+    fake_package_dir = os.path.join(os.path.split(__file__)[0], 'fixtures', 'small_fake_package')
+    fake_package_dir = fake_package_dir.replace('\\', '/')
+    runner = CliRunner()
+    with runner.isolated_filesystem():
+        with open('requirements.in', 'w') as req_in:
+            req_in.write('-e ' + fake_package_dir)  # require editable fake package
+
+        out = runner.invoke(cli, ['-n'])
+
+        print(out.output)
+        assert out.exit_code == 0
+        assert fake_package_dir in out.output
+        assert 'six==1.10.0' in out.output