We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pip-sync appears to get confused about editable packages
When a requirements.txt file includes editable local packages, pip-sync seems to want to uninstall them and reinstall them.

Also, the pip install command that pip-sync attempts to perform is missing the `-e` editable flags, resulting in an error.
##### Steps to replicate
- Create a new virtualenv for testing

``` bash
virtualenv --no-site-packages testvenv
source ./testvenv/bin/activate
pip list
```

returns:

```
pip (8.1.2)
setuptools (28.3.0)
wheel (0.30.0a0)
```
- Generate a requirements.txt file (obfuscated from our actual names):

```
-e file:///vagrant/projects/someproject
```
- Install the editable project into the virtualenv

``` bash
pip install -r requirements.txt --no-deps
```
- Install pip-tools into the venv (because the globally installed pip-tools doesn't seem to deal with virtualenvironents well (another bug?) )

``` bash
pip install pip-tools
deactivate
source ./testvenv/bin/activate
```
- See what pip-sync thinks

``` bash
pip-sync -n requirements.txt
```

returns _unexpected results_:

```
Would uninstall:
  our.package.name
Would install:
  file:///vagrant/projects/packagename
```
- Try to do the sync

```
pip-sync requirements.txt
```

returns

```
Uninstalling our.package.name-1.2.3+dirty:
  Successfully uninstalled our.package.name-1.2.3+dirty
Processing /vagrant/projects/packagename
    Complete output from command python setup.py egg_info:

*** Bunch of errors we generate related to our package's assumptions about being a git repo ***

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-ls2yuR-build/
Traceback (most recent call last):
  File "/home/vagrant/testvenv/bin/pip-sync", line 11, in <module>
    sys.exit(cli())
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py", line 696, in main
    rv = self.invoke(ctx)
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/piptools/scripts/sync.py", line 72, in cli
    install_flags=install_flags))
  File "/home/vagrant/testvenv/local/lib/python2.7/site-packages/piptools/sync.py", line 157, in sync
    check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))
  File "/usr/lib/python2.7/subprocess.py", line 511, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['pip', 'install', 'file:///vagrant/projects/packagename']' returned non-zero exit status 1
```
##### Expected result

A) The tool shouldn't be attempting to uninstall the editable package and then reinstall it
B) Installing editable packages should have a `-e` option


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `piptools/sync.py`
Content:
```
1 import collections
2 import os
3 import sys
4 from subprocess import check_call
5 
6 from . import click
7 from .exceptions import IncompatibleRequirements, UnsupportedConstraint
8 from .utils import flat_map, key_from_req
9 
10 PACKAGES_TO_IGNORE = [
11     'pip',
12     'pip-tools',
13     'pip-review',
14     'setuptools',
15     'wheel',
16 ]
17 
18 
19 def dependency_tree(installed_keys, root_key):
20     """
21     Calculate the dependency tree for the package `root_key` and return
22     a collection of all its dependencies.  Uses a DFS traversal algorithm.
23 
24     `installed_keys` should be a {key: requirement} mapping, e.g.
25         {'django': from_line('django==1.8')}
26     `root_key` should be the key to return the dependency tree for.
27     """
28     dependencies = set()
29     queue = collections.deque()
30 
31     if root_key in installed_keys:
32         dep = installed_keys[root_key]
33         queue.append(dep)
34 
35     while queue:
36         v = queue.popleft()
37         key = key_from_req(v)
38         if key in dependencies:
39             continue
40 
41         dependencies.add(key)
42 
43         for dep_specifier in v.requires():
44             dep_name = key_from_req(dep_specifier)
45             if dep_name in installed_keys:
46                 dep = installed_keys[dep_name]
47 
48                 if dep_specifier.specifier.contains(dep.version):
49                     queue.append(dep)
50 
51     return dependencies
52 
53 
54 def get_dists_to_ignore(installed):
55     """
56     Returns a collection of package names to ignore when performing pip-sync,
57     based on the currently installed environment.  For example, when pip-tools
58     is installed in the local environment, it should be ignored, including all
59     of its dependencies (e.g. click).  When pip-tools is not installed
60     locally, click should also be installed/uninstalled depending on the given
61     requirements.
62     """
63     installed_keys = {key_from_req(r): r for r in installed}
64     return list(flat_map(lambda req: dependency_tree(installed_keys, req), PACKAGES_TO_IGNORE))
65 
66 
67 def merge(requirements, ignore_conflicts):
68     by_key = {}
69 
70     for ireq in requirements:
71         if ireq.link is not None and not ireq.editable:
72             msg = ('pip-compile does not support URLs as packages, unless they are editable. '
73                    'Perhaps add -e option?')
74             raise UnsupportedConstraint(msg, ireq)
75 
76         key = ireq.link or key_from_req(ireq.req)
77 
78         if not ignore_conflicts:
79             existing_ireq = by_key.get(key)
80             if existing_ireq:
81                 # NOTE: We check equality here since we can assume that the
82                 # requirements are all pinned
83                 if ireq.specifier != existing_ireq.specifier:
84                     raise IncompatibleRequirements(ireq, existing_ireq)
85 
86         # TODO: Always pick the largest specifier in case of a conflict
87         by_key[key] = ireq
88 
89     return by_key.values()
90 
91 
92 def diff(compiled_requirements, installed_dists):
93     """
94     Calculate which packages should be installed or uninstalled, given a set
95     of compiled requirements and a list of currently installed modules.
96     """
97     requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}
98 
99     satisfied = set()  # holds keys
100     to_install = set()  # holds keys-and-versions
101     to_uninstall = set()  # holds keys
102 
103     pkgs_to_ignore = get_dists_to_ignore(installed_dists)
104     for dist in installed_dists:
105         key = key_from_req(dist)
106         if key not in requirements_lut:
107             to_uninstall.add(key)
108         elif requirements_lut[key].specifier.contains(dist.version):
109             satisfied.add(key)
110 
111     for key, requirement in requirements_lut.items():
112         if key not in satisfied:
113             to_install.add(str(requirement.link or requirement.req))
114 
115     # Make sure to not uninstall any packages that should be ignored
116     to_uninstall -= set(pkgs_to_ignore)
117 
118     return (to_install, to_uninstall)
119 
120 
121 def sync(to_install, to_uninstall, verbose=False, dry_run=False, pip_flags=None, install_flags=None):
122     """
123     Install and uninstalls the given sets of modules.
124     """
125     if not to_uninstall and not to_install:
126         click.echo("Everything up-to-date")
127 
128     if pip_flags is None:
129         pip_flags = []
130 
131     if not verbose:
132         pip_flags += ['-q']
133 
134     if os.environ.get('VIRTUAL_ENV'):
135         # find pip via PATH
136         pip = 'pip'
137     else:
138         # find pip in same directory as pip-sync entry-point script
139         pip = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'pip')
140 
141     if to_uninstall:
142         if dry_run:
143             click.echo("Would uninstall:")
144             for pkg in to_uninstall:
145                 click.echo("  {}".format(pkg))
146         else:
147             check_call([pip, 'uninstall', '-y'] + pip_flags + sorted(to_uninstall))
148 
149     if to_install:
150         if install_flags is None:
151             install_flags = []
152         if dry_run:
153             click.echo("Would install:")
154             for pkg in to_install:
155                 click.echo("  {}".format(pkg))
156         else:
157             check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))
158     return 0
159
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/piptools/sync.py b/piptools/sync.py
--- a/piptools/sync.py
+++ b/piptools/sync.py
@@ -5,7 +5,7 @@
 
 from . import click
 from .exceptions import IncompatibleRequirements, UnsupportedConstraint
-from .utils import flat_map, key_from_req
+from .utils import flat_map, format_requirement, key_from_req
 
 PACKAGES_TO_IGNORE = [
     'pip',
@@ -97,7 +97,7 @@
     requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}
 
     satisfied = set()  # holds keys
-    to_install = set()  # holds keys-and-versions
+    to_install = set()  # holds InstallRequirement objects
     to_uninstall = set()  # holds keys
 
     pkgs_to_ignore = get_dists_to_ignore(installed_dists)
@@ -110,7 +110,7 @@
 
     for key, requirement in requirements_lut.items():
         if key not in satisfied:
-            to_install.add(str(requirement.link or requirement.req))
+            to_install.add(requirement)
 
     # Make sure to not uninstall any packages that should be ignored
     to_uninstall -= set(pkgs_to_ignore)
@@ -151,8 +151,14 @@
             install_flags = []
         if dry_run:
             click.echo("Would install:")
-            for pkg in to_install:
-                click.echo("  {}".format(pkg))
+            for ireq in to_install:
+                click.echo("  {}".format(format_requirement(ireq)))
         else:
-            check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))
+            package_args = []
+            for ireq in sorted(to_install):
+                if ireq.editable:
+                    package_args.extend(['-e', str(ireq.link or ireq.req)])
+                else:
+                    package_args.append(str(ireq.req))
+            check_call([pip, 'install'] + pip_flags + install_flags + package_args)
     return 0

{"golden_diff": "diff --git a/piptools/sync.py b/piptools/sync.py\n--- a/piptools/sync.py\n+++ b/piptools/sync.py\n@@ -5,7 +5,7 @@\n \n from . import click\n from .exceptions import IncompatibleRequirements, UnsupportedConstraint\n-from .utils import flat_map, key_from_req\n+from .utils import flat_map, format_requirement, key_from_req\n \n PACKAGES_TO_IGNORE = [\n     'pip',\n@@ -97,7 +97,7 @@\n     requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}\n \n     satisfied = set()  # holds keys\n-    to_install = set()  # holds keys-and-versions\n+    to_install = set()  # holds InstallRequirement objects\n     to_uninstall = set()  # holds keys\n \n     pkgs_to_ignore = get_dists_to_ignore(installed_dists)\n@@ -110,7 +110,7 @@\n \n     for key, requirement in requirements_lut.items():\n         if key not in satisfied:\n-            to_install.add(str(requirement.link or requirement.req))\n+            to_install.add(requirement)\n \n     # Make sure to not uninstall any packages that should be ignored\n     to_uninstall -= set(pkgs_to_ignore)\n@@ -151,8 +151,14 @@\n             install_flags = []\n         if dry_run:\n             click.echo(\"Would install:\")\n-            for pkg in to_install:\n-                click.echo(\"  {}\".format(pkg))\n+            for ireq in to_install:\n+                click.echo(\"  {}\".format(format_requirement(ireq)))\n         else:\n-            check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))\n+            package_args = []\n+            for ireq in sorted(to_install):\n+                if ireq.editable:\n+                    package_args.extend(['-e', str(ireq.link or ireq.req)])\n+                else:\n+                    package_args.append(str(ireq.req))\n+            check_call([pip, 'install'] + pip_flags + install_flags + package_args)\n     return 0\n", "issue": "pip-sync appears to get confused about editable packages\nWhen a requirements.txt file includes editable local packages, pip-sync seems to want to uninstall them and reinstall them.\n\nAlso, the pip install command that pip-sync attempts to perform is missing the `-e` editable flags, resulting in an error.\n##### Steps to replicate\n- Create a new virtualenv for testing\n\n``` bash\nvirtualenv --no-site-packages testvenv\nsource ./testvenv/bin/activate\npip list\n```\n\nreturns:\n\n```\npip (8.1.2)\nsetuptools (28.3.0)\nwheel (0.30.0a0)\n```\n- Generate a requirements.txt file (obfuscated from our actual names):\n\n```\n-e file:///vagrant/projects/someproject\n```\n- Install the editable project into the virtualenv\n\n``` bash\npip install -r requirements.txt --no-deps\n```\n- Install pip-tools into the venv (because the globally installed pip-tools doesn't seem to deal with virtualenvironents well (another bug?) )\n\n``` bash\npip install pip-tools\ndeactivate\nsource ./testvenv/bin/activate\n```\n- See what pip-sync thinks\n\n``` bash\npip-sync -n requirements.txt\n```\n\nreturns _unexpected results_:\n\n```\nWould uninstall:\n  our.package.name\nWould install:\n  file:///vagrant/projects/packagename\n```\n- Try to do the sync\n\n```\npip-sync requirements.txt\n```\n\nreturns\n\n```\nUninstalling our.package.name-1.2.3+dirty:\n  Successfully uninstalled our.package.name-1.2.3+dirty\nProcessing /vagrant/projects/packagename\n    Complete output from command python setup.py egg_info:\n\n*** Bunch of errors we generate related to our package's assumptions about being a git repo ***\n\n    ----------------------------------------\nCommand \"python setup.py egg_info\" failed with error code 1 in /tmp/pip-ls2yuR-build/\nTraceback (most recent call last):\n  File \"/home/vagrant/testvenv/bin/pip-sync\", line 11, in <module>\n    sys.exit(cli())\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py\", line 716, in __call__\n    return self.main(*args, **kwargs)\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py\", line 696, in main\n    rv = self.invoke(ctx)\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py\", line 889, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/click/core.py\", line 534, in invoke\n    return callback(*args, **kwargs)\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/piptools/scripts/sync.py\", line 72, in cli\n    install_flags=install_flags))\n  File \"/home/vagrant/testvenv/local/lib/python2.7/site-packages/piptools/sync.py\", line 157, in sync\n    check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))\n  File \"/usr/lib/python2.7/subprocess.py\", line 511, in check_call\n    raise CalledProcessError(retcode, cmd)\nsubprocess.CalledProcessError: Command '['pip', 'install', 'file:///vagrant/projects/packagename']' returned non-zero exit status 1\n```\n##### Expected result\n\nA) The tool shouldn't be attempting to uninstall the editable package and then reinstall it\nB) Installing editable packages should have a `-e` option\n\n", "before_files": [{"content": "import collections\nimport os\nimport sys\nfrom subprocess import check_call\n\nfrom . import click\nfrom .exceptions import IncompatibleRequirements, UnsupportedConstraint\nfrom .utils import flat_map, key_from_req\n\nPACKAGES_TO_IGNORE = [\n    'pip',\n    'pip-tools',\n    'pip-review',\n    'setuptools',\n    'wheel',\n]\n\n\ndef dependency_tree(installed_keys, root_key):\n    \"\"\"\n    Calculate the dependency tree for the package `root_key` and return\n    a collection of all its dependencies.  Uses a DFS traversal algorithm.\n\n    `installed_keys` should be a {key: requirement} mapping, e.g.\n        {'django': from_line('django==1.8')}\n    `root_key` should be the key to return the dependency tree for.\n    \"\"\"\n    dependencies = set()\n    queue = collections.deque()\n\n    if root_key in installed_keys:\n        dep = installed_keys[root_key]\n        queue.append(dep)\n\n    while queue:\n        v = queue.popleft()\n        key = key_from_req(v)\n        if key in dependencies:\n            continue\n\n        dependencies.add(key)\n\n        for dep_specifier in v.requires():\n            dep_name = key_from_req(dep_specifier)\n            if dep_name in installed_keys:\n                dep = installed_keys[dep_name]\n\n                if dep_specifier.specifier.contains(dep.version):\n                    queue.append(dep)\n\n    return dependencies\n\n\ndef get_dists_to_ignore(installed):\n    \"\"\"\n    Returns a collection of package names to ignore when performing pip-sync,\n    based on the currently installed environment.  For example, when pip-tools\n    is installed in the local environment, it should be ignored, including all\n    of its dependencies (e.g. click).  When pip-tools is not installed\n    locally, click should also be installed/uninstalled depending on the given\n    requirements.\n    \"\"\"\n    installed_keys = {key_from_req(r): r for r in installed}\n    return list(flat_map(lambda req: dependency_tree(installed_keys, req), PACKAGES_TO_IGNORE))\n\n\ndef merge(requirements, ignore_conflicts):\n    by_key = {}\n\n    for ireq in requirements:\n        if ireq.link is not None and not ireq.editable:\n            msg = ('pip-compile does not support URLs as packages, unless they are editable. '\n                   'Perhaps add -e option?')\n            raise UnsupportedConstraint(msg, ireq)\n\n        key = ireq.link or key_from_req(ireq.req)\n\n        if not ignore_conflicts:\n            existing_ireq = by_key.get(key)\n            if existing_ireq:\n                # NOTE: We check equality here since we can assume that the\n                # requirements are all pinned\n                if ireq.specifier != existing_ireq.specifier:\n                    raise IncompatibleRequirements(ireq, existing_ireq)\n\n        # TODO: Always pick the largest specifier in case of a conflict\n        by_key[key] = ireq\n\n    return by_key.values()\n\n\ndef diff(compiled_requirements, installed_dists):\n    \"\"\"\n    Calculate which packages should be installed or uninstalled, given a set\n    of compiled requirements and a list of currently installed modules.\n    \"\"\"\n    requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}\n\n    satisfied = set()  # holds keys\n    to_install = set()  # holds keys-and-versions\n    to_uninstall = set()  # holds keys\n\n    pkgs_to_ignore = get_dists_to_ignore(installed_dists)\n    for dist in installed_dists:\n        key = key_from_req(dist)\n        if key not in requirements_lut:\n            to_uninstall.add(key)\n        elif requirements_lut[key].specifier.contains(dist.version):\n            satisfied.add(key)\n\n    for key, requirement in requirements_lut.items():\n        if key not in satisfied:\n            to_install.add(str(requirement.link or requirement.req))\n\n    # Make sure to not uninstall any packages that should be ignored\n    to_uninstall -= set(pkgs_to_ignore)\n\n    return (to_install, to_uninstall)\n\n\ndef sync(to_install, to_uninstall, verbose=False, dry_run=False, pip_flags=None, install_flags=None):\n    \"\"\"\n    Install and uninstalls the given sets of modules.\n    \"\"\"\n    if not to_uninstall and not to_install:\n        click.echo(\"Everything up-to-date\")\n\n    if pip_flags is None:\n        pip_flags = []\n\n    if not verbose:\n        pip_flags += ['-q']\n\n    if os.environ.get('VIRTUAL_ENV'):\n        # find pip via PATH\n        pip = 'pip'\n    else:\n        # find pip in same directory as pip-sync entry-point script\n        pip = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'pip')\n\n    if to_uninstall:\n        if dry_run:\n            click.echo(\"Would uninstall:\")\n            for pkg in to_uninstall:\n                click.echo(\"  {}\".format(pkg))\n        else:\n            check_call([pip, 'uninstall', '-y'] + pip_flags + sorted(to_uninstall))\n\n    if to_install:\n        if install_flags is None:\n            install_flags = []\n        if dry_run:\n            click.echo(\"Would install:\")\n            for pkg in to_install:\n                click.echo(\"  {}\".format(pkg))\n        else:\n            check_call([pip, 'install'] + pip_flags + install_flags + sorted(to_install))\n    return 0\n", "path": "piptools/sync.py"}], "after_files": [{"content": "import collections\nimport os\nimport sys\nfrom subprocess import check_call\n\nfrom . import click\nfrom .exceptions import IncompatibleRequirements, UnsupportedConstraint\nfrom .utils import flat_map, format_requirement, key_from_req\n\nPACKAGES_TO_IGNORE = [\n    'pip',\n    'pip-tools',\n    'pip-review',\n    'setuptools',\n    'wheel',\n]\n\n\ndef dependency_tree(installed_keys, root_key):\n    \"\"\"\n    Calculate the dependency tree for the package `root_key` and return\n    a collection of all its dependencies.  Uses a DFS traversal algorithm.\n\n    `installed_keys` should be a {key: requirement} mapping, e.g.\n        {'django': from_line('django==1.8')}\n    `root_key` should be the key to return the dependency tree for.\n    \"\"\"\n    dependencies = set()\n    queue = collections.deque()\n\n    if root_key in installed_keys:\n        dep = installed_keys[root_key]\n        queue.append(dep)\n\n    while queue:\n        v = queue.popleft()\n        key = key_from_req(v)\n        if key in dependencies:\n            continue\n\n        dependencies.add(key)\n\n        for dep_specifier in v.requires():\n            dep_name = key_from_req(dep_specifier)\n            if dep_name in installed_keys:\n                dep = installed_keys[dep_name]\n\n                if dep_specifier.specifier.contains(dep.version):\n                    queue.append(dep)\n\n    return dependencies\n\n\ndef get_dists_to_ignore(installed):\n    \"\"\"\n    Returns a collection of package names to ignore when performing pip-sync,\n    based on the currently installed environment.  For example, when pip-tools\n    is installed in the local environment, it should be ignored, including all\n    of its dependencies (e.g. click).  When pip-tools is not installed\n    locally, click should also be installed/uninstalled depending on the given\n    requirements.\n    \"\"\"\n    installed_keys = {key_from_req(r): r for r in installed}\n    return list(flat_map(lambda req: dependency_tree(installed_keys, req), PACKAGES_TO_IGNORE))\n\n\ndef merge(requirements, ignore_conflicts):\n    by_key = {}\n\n    for ireq in requirements:\n        if ireq.link is not None and not ireq.editable:\n            msg = ('pip-compile does not support URLs as packages, unless they are editable. '\n                   'Perhaps add -e option?')\n            raise UnsupportedConstraint(msg, ireq)\n\n        key = ireq.link or key_from_req(ireq.req)\n\n        if not ignore_conflicts:\n            existing_ireq = by_key.get(key)\n            if existing_ireq:\n                # NOTE: We check equality here since we can assume that the\n                # requirements are all pinned\n                if ireq.specifier != existing_ireq.specifier:\n                    raise IncompatibleRequirements(ireq, existing_ireq)\n\n        # TODO: Always pick the largest specifier in case of a conflict\n        by_key[key] = ireq\n\n    return by_key.values()\n\n\ndef diff(compiled_requirements, installed_dists):\n    \"\"\"\n    Calculate which packages should be installed or uninstalled, given a set\n    of compiled requirements and a list of currently installed modules.\n    \"\"\"\n    requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}\n\n    satisfied = set()  # holds keys\n    to_install = set()  # holds InstallRequirement objects\n    to_uninstall = set()  # holds keys\n\n    pkgs_to_ignore = get_dists_to_ignore(installed_dists)\n    for dist in installed_dists:\n        key = key_from_req(dist)\n        if key not in requirements_lut:\n            to_uninstall.add(key)\n        elif requirements_lut[key].specifier.contains(dist.version):\n            satisfied.add(key)\n\n    for key, requirement in requirements_lut.items():\n        if key not in satisfied:\n            to_install.add(requirement)\n\n    # Make sure to not uninstall any packages that should be ignored\n    to_uninstall -= set(pkgs_to_ignore)\n\n    return (to_install, to_uninstall)\n\n\ndef sync(to_install, to_uninstall, verbose=False, dry_run=False, pip_flags=None, install_flags=None):\n    \"\"\"\n    Install and uninstalls the given sets of modules.\n    \"\"\"\n    if not to_uninstall and not to_install:\n        click.echo(\"Everything up-to-date\")\n\n    if pip_flags is None:\n        pip_flags = []\n\n    if not verbose:\n        pip_flags += ['-q']\n\n    if os.environ.get('VIRTUAL_ENV'):\n        # find pip via PATH\n        pip = 'pip'\n    else:\n        # find pip in same directory as pip-sync entry-point script\n        pip = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'pip')\n\n    if to_uninstall:\n        if dry_run:\n            click.echo(\"Would uninstall:\")\n            for pkg in to_uninstall:\n                click.echo(\"  {}\".format(pkg))\n        else:\n            check_call([pip, 'uninstall', '-y'] + pip_flags + sorted(to_uninstall))\n\n    if to_install:\n        if install_flags is None:\n            install_flags = []\n        if dry_run:\n            click.echo(\"Would install:\")\n            for ireq in to_install:\n                click.echo(\"  {}\".format(format_requirement(ireq)))\n        else:\n            package_args = []\n            for ireq in sorted(to_install):\n                if ireq.editable:\n                    package_args.extend(['-e', str(ireq.link or ireq.req)])\n                else:\n                    package_args.append(str(ireq.req))\n            check_call([pip, 'install'] + pip_flags + install_flags + package_args)\n    return 0\n", "path": "piptools/sync.py"}]}