We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
additonal_dependencies isn't "rollback safe"
Using old pre-commit + a hook repo with `additional_dependencies` it'll happily create the repo without installing the additional dependencies.  Upon upgrading to a newer pre-commit, it doesn't know that the additional dependencies aren't installed yet and will happily attempt to run in there (usually causing an executable to not be found).  We need some way to signify when these have been installed in order for this to be rollable.  A workaround is to `pre-commit clean` when upgrading, but that kinda is not the best (and especially confusing).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `setup.py`
Content:
```
1 from setuptools import find_packages
2 from setuptools import setup
3 
4 
5 setup(
6     name='pre_commit',
7     description=(
8         'A framework for managing and maintaining multi-language pre-commit '
9         'hooks.'
10     ),
11     url='https://github.com/pre-commit/pre-commit',
12     version='0.6.8',
13 
14     author='Anthony Sottile',
15     author_email='asottile@umich.edu',
16 
17     platforms='linux',
18     classifiers=[
19         'License :: OSI Approved :: MIT License',
20         'Programming Language :: Python :: 2',
21         'Programming Language :: Python :: 2.6',
22         'Programming Language :: Python :: 2.7',
23         'Programming Language :: Python :: 3',
24         'Programming Language :: Python :: 3.3',
25         'Programming Language :: Python :: 3.4',
26         'Programming Language :: Python :: 3.5',
27         'Programming Language :: Python :: Implementation :: CPython',
28         'Programming Language :: Python :: Implementation :: PyPy',
29     ],
30 
31     packages=find_packages('.', exclude=('tests*', 'testing*')),
32     package_data={
33         'pre_commit': [
34             'resources/hook-tmpl',
35             'resources/pre-push-tmpl',
36             'resources/rbenv.tar.gz',
37             'resources/ruby-build.tar.gz',
38             'resources/ruby-download.tar.gz',
39         ]
40     },
41     install_requires=[
42         'argparse',
43         'aspy.yaml',
44         'cached-property',
45         'jsonschema',
46         'nodeenv>=0.11.1',
47         'ordereddict',
48         'pyyaml',
49         'simplejson',
50         'virtualenv',
51     ],
52     entry_points={
53         'console_scripts': [
54             'pre-commit = pre_commit.main:main',
55             'pre-commit-validate-config = pre_commit.clientlib.validate_config:run',  # noqa
56             'pre-commit-validate-manifest = pre_commit.clientlib.validate_manifest:run',  # noqa
57         ],
58     },
59 )
60
```

Path: `pre_commit/repository.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import logging
4 import shutil
5 from collections import defaultdict
6 
7 import pkg_resources
8 from cached_property import cached_property
9 
10 from pre_commit import git
11 from pre_commit.clientlib.validate_config import is_local_hooks
12 from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
13 from pre_commit.jsonschema_extensions import apply_defaults
14 from pre_commit.languages.all import languages
15 from pre_commit.languages.helpers import environment_dir
16 from pre_commit.manifest import Manifest
17 from pre_commit.prefixed_command_runner import PrefixedCommandRunner
18 
19 
20 logger = logging.getLogger('pre_commit')
21 
22 _pre_commit_version = pkg_resources.parse_version(
23     pkg_resources.get_distribution('pre-commit').version
24 )
25 
26 
27 class Repository(object):
28     def __init__(self, repo_config, repo_path_getter):
29         self.repo_config = repo_config
30         self.repo_path_getter = repo_path_getter
31         self.__installed = False
32 
33     @classmethod
34     def create(cls, config, store):
35         if is_local_hooks(config):
36             return LocalRepository(config)
37         else:
38             repo_path_getter = store.get_repo_path_getter(
39                 config['repo'], config['sha']
40             )
41             return cls(config, repo_path_getter)
42 
43     @cached_property
44     def repo_url(self):
45         return self.repo_config['repo']
46 
47     @cached_property
48     def sha(self):
49         return self.repo_config['sha']
50 
51     @cached_property
52     def languages(self):
53         return set(
54             (hook['language'], hook['language_version'])
55             for _, hook in self.hooks
56         )
57 
58     @cached_property
59     def additional_dependencies(self):
60         dep_dict = defaultdict(lambda: defaultdict(set))
61         for _, hook in self.hooks:
62             dep_dict[hook['language']][hook['language_version']].update(
63                 hook.get('additional_dependencies', []),
64             )
65         return dep_dict
66 
67     @cached_property
68     def hooks(self):
69         for hook in self.repo_config['hooks']:
70             if hook['id'] not in self.manifest.hooks:
71                 logger.error(
72                     '`{0}` is not present in repository {1}.  '
73                     'Typo? Perhaps it is introduced in a newer version?  '
74                     'Often `pre-commit autoupdate` fixes this.'.format(
75                         hook['id'], self.repo_config['repo'],
76                     )
77                 )
78                 exit(1)
79             hook_version = pkg_resources.parse_version(
80                 self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],
81             )
82             if hook_version > _pre_commit_version:
83                 logger.error(
84                     'The hook `{0}` requires pre-commit version {1} but '
85                     'version {2} is installed.  '
86                     'Perhaps run `pip install --upgrade pre-commit`.'.format(
87                         hook['id'], hook_version, _pre_commit_version,
88                     )
89                 )
90                 exit(1)
91         return tuple(
92             (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
93             for hook in self.repo_config['hooks']
94         )
95 
96     @cached_property
97     def manifest(self):
98         return Manifest(self.repo_path_getter)
99 
100     @cached_property
101     def cmd_runner(self):
102         return PrefixedCommandRunner(self.repo_path_getter.repo_path)
103 
104     def require_installed(self):
105         if self.__installed:
106             return
107 
108         self.install()
109         self.__installed = True
110 
111     def install(self):
112         """Install the hook repository."""
113         def language_is_installed(language_name, language_version):
114             language = languages[language_name]
115             directory = environment_dir(
116                 language.ENVIRONMENT_DIR, language_version,
117             )
118             return (
119                 directory is None or
120                 self.cmd_runner.exists(directory, '.installed')
121             )
122 
123         if not all(
124             language_is_installed(language_name, language_version)
125             for language_name, language_version in self.languages
126         ):
127             logger.info(
128                 'Installing environment for {0}.'.format(self.repo_url)
129             )
130             logger.info('Once installed this environment will be reused.')
131             logger.info('This may take a few minutes...')
132 
133         for language_name, language_version in self.languages:
134             language = languages[language_name]
135             if language_is_installed(language_name, language_version):
136                 continue
137 
138             directory = environment_dir(
139                 language.ENVIRONMENT_DIR, language_version,
140             )
141             # There's potentially incomplete cleanup from previous runs
142             # Clean it up!
143             if self.cmd_runner.exists(directory):
144                 shutil.rmtree(self.cmd_runner.path(directory))
145 
146             language.install_environment(
147                 self.cmd_runner, language_version,
148                 self.additional_dependencies[language_name][language_version],
149             )
150             # Touch the .installed file (atomic) to indicate we've installed
151             open(self.cmd_runner.path(directory, '.installed'), 'w').close()
152 
153     def run_hook(self, hook, file_args):
154         """Run a hook.
155 
156         Args:
157             hook - Hook dictionary
158             file_args - List of files to run
159         """
160         self.require_installed()
161         return languages[hook['language']].run_hook(
162             self.cmd_runner, hook, file_args,
163         )
164 
165 
166 class LocalRepository(Repository):
167     def __init__(self, repo_config):
168         super(LocalRepository, self).__init__(repo_config, None)
169 
170     @cached_property
171     def hooks(self):
172         return tuple(
173             (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))
174             for hook in self.repo_config['hooks']
175         )
176 
177     @cached_property
178     def cmd_runner(self):
179         return PrefixedCommandRunner(git.get_root())
180 
181     @cached_property
182     def sha(self):
183         raise NotImplementedError
184 
185     @cached_property
186     def manifest(self):
187         raise NotImplementedError
188
```
--- 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/pre_commit/repository.py b/pre_commit/repository.py
--- a/pre_commit/repository.py
+++ b/pre_commit/repository.py
@@ -1,12 +1,16 @@
 from __future__ import unicode_literals
 
+import io
+import json
 import logging
+import os
 import shutil
 from collections import defaultdict
 
 import pkg_resources
 from cached_property import cached_property
 
+from pre_commit import five
 from pre_commit import git
 from pre_commit.clientlib.validate_config import is_local_hooks
 from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
@@ -23,6 +27,9 @@
     pkg_resources.get_distribution('pre-commit').version
 )
 
+# Bump when installation changes in a backwards / forwards incompatible way
+INSTALLED_STATE_VERSION = '1'
+
 
 class Repository(object):
     def __init__(self, repo_config, repo_path_getter):
@@ -110,14 +117,45 @@
 
     def install(self):
         """Install the hook repository."""
+        def state(language_name, language_version):
+            return {
+                'additional_dependencies': sorted(
+                    self.additional_dependencies[
+                        language_name
+                    ][language_version],
+                )
+            }
+
+        def state_filename(venv, suffix=''):
+            return self.cmd_runner.path(
+                venv, '.install_state_v' + INSTALLED_STATE_VERSION + suffix,
+            )
+
+        def read_state(venv):
+            if not os.path.exists(state_filename(venv)):
+                return None
+            else:
+                return json.loads(io.open(state_filename(venv)).read())
+
+        def write_state(venv, language_name, language_version):
+            with io.open(
+                    state_filename(venv, suffix='staging'), 'w',
+            ) as state_file:
+                state_file.write(five.to_text(json.dumps(
+                    state(language_name, language_version),
+                )))
+            # Move the file into place atomically to indicate we've installed
+            os.rename(
+                state_filename(venv, suffix='staging'),
+                state_filename(venv),
+            )
+
         def language_is_installed(language_name, language_version):
             language = languages[language_name]
-            directory = environment_dir(
-                language.ENVIRONMENT_DIR, language_version,
-            )
+            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
             return (
-                directory is None or
-                self.cmd_runner.exists(directory, '.installed')
+                venv is None or
+                read_state(venv) == state(language_name, language_version)
             )
 
         if not all(
@@ -131,24 +169,23 @@
             logger.info('This may take a few minutes...')
 
         for language_name, language_version in self.languages:
-            language = languages[language_name]
             if language_is_installed(language_name, language_version):
                 continue
 
-            directory = environment_dir(
-                language.ENVIRONMENT_DIR, language_version,
-            )
+            language = languages[language_name]
+            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)
+
             # There's potentially incomplete cleanup from previous runs
             # Clean it up!
-            if self.cmd_runner.exists(directory):
-                shutil.rmtree(self.cmd_runner.path(directory))
+            if self.cmd_runner.exists(venv):
+                shutil.rmtree(self.cmd_runner.path(venv))
 
             language.install_environment(
                 self.cmd_runner, language_version,
                 self.additional_dependencies[language_name][language_version],
             )
-            # Touch the .installed file (atomic) to indicate we've installed
-            open(self.cmd_runner.path(directory, '.installed'), 'w').close()
+            # Write our state to indicate we're installed
+            write_state(venv, language_name, language_version)
 
     def run_hook(self, hook, file_args):
         """Run a hook.
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -46,7 +46,6 @@
         'nodeenv>=0.11.1',
         'ordereddict',
         'pyyaml',
-        'simplejson',
         'virtualenv',
     ],
     entry_points={

{"golden_diff": "diff --git a/pre_commit/repository.py b/pre_commit/repository.py\n--- a/pre_commit/repository.py\n+++ b/pre_commit/repository.py\n@@ -1,12 +1,16 @@\n from __future__ import unicode_literals\n \n+import io\n+import json\n import logging\n+import os\n import shutil\n from collections import defaultdict\n \n import pkg_resources\n from cached_property import cached_property\n \n+from pre_commit import five\n from pre_commit import git\n from pre_commit.clientlib.validate_config import is_local_hooks\n from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\n@@ -23,6 +27,9 @@\n     pkg_resources.get_distribution('pre-commit').version\n )\n \n+# Bump when installation changes in a backwards / forwards incompatible way\n+INSTALLED_STATE_VERSION = '1'\n+\n \n class Repository(object):\n     def __init__(self, repo_config, repo_path_getter):\n@@ -110,14 +117,45 @@\n \n     def install(self):\n         \"\"\"Install the hook repository.\"\"\"\n+        def state(language_name, language_version):\n+            return {\n+                'additional_dependencies': sorted(\n+                    self.additional_dependencies[\n+                        language_name\n+                    ][language_version],\n+                )\n+            }\n+\n+        def state_filename(venv, suffix=''):\n+            return self.cmd_runner.path(\n+                venv, '.install_state_v' + INSTALLED_STATE_VERSION + suffix,\n+            )\n+\n+        def read_state(venv):\n+            if not os.path.exists(state_filename(venv)):\n+                return None\n+            else:\n+                return json.loads(io.open(state_filename(venv)).read())\n+\n+        def write_state(venv, language_name, language_version):\n+            with io.open(\n+                    state_filename(venv, suffix='staging'), 'w',\n+            ) as state_file:\n+                state_file.write(five.to_text(json.dumps(\n+                    state(language_name, language_version),\n+                )))\n+            # Move the file into place atomically to indicate we've installed\n+            os.rename(\n+                state_filename(venv, suffix='staging'),\n+                state_filename(venv),\n+            )\n+\n         def language_is_installed(language_name, language_version):\n             language = languages[language_name]\n-            directory = environment_dir(\n-                language.ENVIRONMENT_DIR, language_version,\n-            )\n+            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)\n             return (\n-                directory is None or\n-                self.cmd_runner.exists(directory, '.installed')\n+                venv is None or\n+                read_state(venv) == state(language_name, language_version)\n             )\n \n         if not all(\n@@ -131,24 +169,23 @@\n             logger.info('This may take a few minutes...')\n \n         for language_name, language_version in self.languages:\n-            language = languages[language_name]\n             if language_is_installed(language_name, language_version):\n                 continue\n \n-            directory = environment_dir(\n-                language.ENVIRONMENT_DIR, language_version,\n-            )\n+            language = languages[language_name]\n+            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)\n+\n             # There's potentially incomplete cleanup from previous runs\n             # Clean it up!\n-            if self.cmd_runner.exists(directory):\n-                shutil.rmtree(self.cmd_runner.path(directory))\n+            if self.cmd_runner.exists(venv):\n+                shutil.rmtree(self.cmd_runner.path(venv))\n \n             language.install_environment(\n                 self.cmd_runner, language_version,\n                 self.additional_dependencies[language_name][language_version],\n             )\n-            # Touch the .installed file (atomic) to indicate we've installed\n-            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n+            # Write our state to indicate we're installed\n+            write_state(venv, language_name, language_version)\n \n     def run_hook(self, hook, file_args):\n         \"\"\"Run a hook.\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -46,7 +46,6 @@\n         'nodeenv>=0.11.1',\n         'ordereddict',\n         'pyyaml',\n-        'simplejson',\n         'virtualenv',\n     ],\n     entry_points={\n", "issue": "additonal_dependencies isn't \"rollback safe\"\nUsing old pre-commit + a hook repo with `additional_dependencies` it'll happily create the repo without installing the additional dependencies.  Upon upgrading to a newer pre-commit, it doesn't know that the additional dependencies aren't installed yet and will happily attempt to run in there (usually causing an executable to not be found).  We need some way to signify when these have been installed in order for this to be rollable.  A workaround is to `pre-commit clean` when upgrading, but that kinda is not the best (and especially confusing).\n\n", "before_files": [{"content": "from setuptools import find_packages\nfrom setuptools import setup\n\n\nsetup(\n    name='pre_commit',\n    description=(\n        'A framework for managing and maintaining multi-language pre-commit '\n        'hooks.'\n    ),\n    url='https://github.com/pre-commit/pre-commit',\n    version='0.6.8',\n\n    author='Anthony Sottile',\n    author_email='asottile@umich.edu',\n\n    platforms='linux',\n    classifiers=[\n        'License :: OSI Approved :: MIT License',\n        'Programming Language :: Python :: 2',\n        'Programming Language :: Python :: 2.6',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.3',\n        'Programming Language :: Python :: 3.4',\n        'Programming Language :: Python :: 3.5',\n        'Programming Language :: Python :: Implementation :: CPython',\n        'Programming Language :: Python :: Implementation :: PyPy',\n    ],\n\n    packages=find_packages('.', exclude=('tests*', 'testing*')),\n    package_data={\n        'pre_commit': [\n            'resources/hook-tmpl',\n            'resources/pre-push-tmpl',\n            'resources/rbenv.tar.gz',\n            'resources/ruby-build.tar.gz',\n            'resources/ruby-download.tar.gz',\n        ]\n    },\n    install_requires=[\n        'argparse',\n        'aspy.yaml',\n        'cached-property',\n        'jsonschema',\n        'nodeenv>=0.11.1',\n        'ordereddict',\n        'pyyaml',\n        'simplejson',\n        'virtualenv',\n    ],\n    entry_points={\n        'console_scripts': [\n            'pre-commit = pre_commit.main:main',\n            'pre-commit-validate-config = pre_commit.clientlib.validate_config:run',  # noqa\n            'pre-commit-validate-manifest = pre_commit.clientlib.validate_manifest:run',  # noqa\n        ],\n    },\n)\n", "path": "setup.py"}, {"content": "from __future__ import unicode_literals\n\nimport logging\nimport shutil\nfrom collections import defaultdict\n\nimport pkg_resources\nfrom cached_property import cached_property\n\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n_pre_commit_version = pkg_resources.parse_version(\n    pkg_resources.get_distribution('pre-commit').version\n)\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def additional_dependencies(self):\n        dep_dict = defaultdict(lambda: defaultdict(set))\n        for _, hook in self.hooks:\n            dep_dict[hook['language']][hook['language_version']].update(\n                hook.get('additional_dependencies', []),\n            )\n        return dep_dict\n\n    @cached_property\n    def hooks(self):\n        for hook in self.repo_config['hooks']:\n            if hook['id'] not in self.manifest.hooks:\n                logger.error(\n                    '`{0}` is not present in repository {1}.  '\n                    'Typo? Perhaps it is introduced in a newer version?  '\n                    'Often `pre-commit autoupdate` fixes this.'.format(\n                        hook['id'], self.repo_config['repo'],\n                    )\n                )\n                exit(1)\n            hook_version = pkg_resources.parse_version(\n                self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],\n            )\n            if hook_version > _pre_commit_version:\n                logger.error(\n                    'The hook `{0}` requires pre-commit version {1} but '\n                    'version {2} is installed.  '\n                    'Perhaps run `pip install --upgrade pre-commit`.'.format(\n                        hook['id'], hook_version, _pre_commit_version,\n                    )\n                )\n                exit(1)\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            return (\n                directory is None or\n                self.cmd_runner.exists(directory, '.installed')\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            language = languages[language_name]\n            if language_is_installed(language_name, language_version):\n                continue\n\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(directory):\n                shutil.rmtree(self.cmd_runner.path(directory))\n\n            language.install_environment(\n                self.cmd_runner, language_version,\n                self.additional_dependencies[language_name][language_version],\n            )\n            # Touch the .installed file (atomic) to indicate we've installed\n            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config):\n        super(LocalRepository, self).__init__(repo_config, None)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}], "after_files": [{"content": "from setuptools import find_packages\nfrom setuptools import setup\n\n\nsetup(\n    name='pre_commit',\n    description=(\n        'A framework for managing and maintaining multi-language pre-commit '\n        'hooks.'\n    ),\n    url='https://github.com/pre-commit/pre-commit',\n    version='0.6.8',\n\n    author='Anthony Sottile',\n    author_email='asottile@umich.edu',\n\n    platforms='linux',\n    classifiers=[\n        'License :: OSI Approved :: MIT License',\n        'Programming Language :: Python :: 2',\n        'Programming Language :: Python :: 2.6',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.3',\n        'Programming Language :: Python :: 3.4',\n        'Programming Language :: Python :: 3.5',\n        'Programming Language :: Python :: Implementation :: CPython',\n        'Programming Language :: Python :: Implementation :: PyPy',\n    ],\n\n    packages=find_packages('.', exclude=('tests*', 'testing*')),\n    package_data={\n        'pre_commit': [\n            'resources/hook-tmpl',\n            'resources/pre-push-tmpl',\n            'resources/rbenv.tar.gz',\n            'resources/ruby-build.tar.gz',\n            'resources/ruby-download.tar.gz',\n        ]\n    },\n    install_requires=[\n        'argparse',\n        'aspy.yaml',\n        'cached-property',\n        'jsonschema',\n        'nodeenv>=0.11.1',\n        'ordereddict',\n        'pyyaml',\n        'virtualenv',\n    ],\n    entry_points={\n        'console_scripts': [\n            'pre-commit = pre_commit.main:main',\n            'pre-commit-validate-config = pre_commit.clientlib.validate_config:run',  # noqa\n            'pre-commit-validate-manifest = pre_commit.clientlib.validate_manifest:run',  # noqa\n        ],\n    },\n)\n", "path": "setup.py"}, {"content": "from __future__ import unicode_literals\n\nimport io\nimport json\nimport logging\nimport os\nimport shutil\nfrom collections import defaultdict\n\nimport pkg_resources\nfrom cached_property import cached_property\n\nfrom pre_commit import five\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n_pre_commit_version = pkg_resources.parse_version(\n    pkg_resources.get_distribution('pre-commit').version\n)\n\n# Bump when installation changes in a backwards / forwards incompatible way\nINSTALLED_STATE_VERSION = '1'\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def additional_dependencies(self):\n        dep_dict = defaultdict(lambda: defaultdict(set))\n        for _, hook in self.hooks:\n            dep_dict[hook['language']][hook['language_version']].update(\n                hook.get('additional_dependencies', []),\n            )\n        return dep_dict\n\n    @cached_property\n    def hooks(self):\n        for hook in self.repo_config['hooks']:\n            if hook['id'] not in self.manifest.hooks:\n                logger.error(\n                    '`{0}` is not present in repository {1}.  '\n                    'Typo? Perhaps it is introduced in a newer version?  '\n                    'Often `pre-commit autoupdate` fixes this.'.format(\n                        hook['id'], self.repo_config['repo'],\n                    )\n                )\n                exit(1)\n            hook_version = pkg_resources.parse_version(\n                self.manifest.hooks[hook['id']]['minimum_pre_commit_version'],\n            )\n            if hook_version > _pre_commit_version:\n                logger.error(\n                    'The hook `{0}` requires pre-commit version {1} but '\n                    'version {2} is installed.  '\n                    'Perhaps run `pip install --upgrade pre-commit`.'.format(\n                        hook['id'], hook_version, _pre_commit_version,\n                    )\n                )\n                exit(1)\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def state(language_name, language_version):\n            return {\n                'additional_dependencies': sorted(\n                    self.additional_dependencies[\n                        language_name\n                    ][language_version],\n                )\n            }\n\n        def state_filename(venv, suffix=''):\n            return self.cmd_runner.path(\n                venv, '.install_state_v' + INSTALLED_STATE_VERSION + suffix,\n            )\n\n        def read_state(venv):\n            if not os.path.exists(state_filename(venv)):\n                return None\n            else:\n                return json.loads(io.open(state_filename(venv)).read())\n\n        def write_state(venv, language_name, language_version):\n            with io.open(\n                    state_filename(venv, suffix='staging'), 'w',\n            ) as state_file:\n                state_file.write(five.to_text(json.dumps(\n                    state(language_name, language_version),\n                )))\n            # Move the file into place atomically to indicate we've installed\n            os.rename(\n                state_filename(venv, suffix='staging'),\n                state_filename(venv),\n            )\n\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)\n            return (\n                venv is None or\n                read_state(venv) == state(language_name, language_version)\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            if language_is_installed(language_name, language_version):\n                continue\n\n            language = languages[language_name]\n            venv = environment_dir(language.ENVIRONMENT_DIR, language_version)\n\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(venv):\n                shutil.rmtree(self.cmd_runner.path(venv))\n\n            language.install_environment(\n                self.cmd_runner, language_version,\n                self.additional_dependencies[language_name][language_version],\n            )\n            # Write our state to indicate we're installed\n            write_state(venv, language_name, language_version)\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config):\n        super(LocalRepository, self).__init__(repo_config, None)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}]}