We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cleanup exception that are not logged as error
After #4495 got merged @agjohnson suggested to have an attribute in the Exception class and check for that attribute before log the exception, instead of defining a list for the warning exceptions as I did at:

https://github.com/rtfd/readthedocs.org/pull/4495/files#diff-ca52b098301dd315a834b3556ab9a7d5R424

Also, there are more exceptions that have to treat in the same way: `ProjectConfigurationError` for example.

https://sentry.io/read-the-docs/readthedocs-org/issues/668248681/

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/vcs_support/base.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 """Base classes for VCS backends."""
4 import logging
5 import os
6 import shutil
7 
8 
9 log = logging.getLogger(__name__)
10 
11 
12 class VCSVersion:
13 
14     """
15     Represents a Version (tag or branch) in a VCS.
16 
17     This class should only be instantiated in BaseVCS subclasses.
18 
19     It can act as a context manager to temporarily switch to this tag (eg to
20     build docs for this tag).
21     """
22 
23     def __init__(self, repository, identifier, verbose_name):
24         self.repository = repository
25         self.identifier = identifier
26         self.verbose_name = verbose_name
27 
28     def __repr__(self):
29         return '<VCSVersion: {}:{}'.format(
30             self.repository.repo_url,
31             self.verbose_name,
32         )
33 
34 
35 class BaseVCS:
36 
37     """
38     Base for VCS Classes.
39 
40     VCS commands are ran inside a ``LocalEnvironment``.
41     """
42 
43     supports_tags = False  # Whether this VCS supports tags or not.
44     supports_branches = False  # Whether this VCS supports branches or not.
45     supports_submodules = False
46 
47     # =========================================================================
48     # General methods
49     # =========================================================================
50 
51     # Defining a base API, so we'll have unused args
52     # pylint: disable=unused-argument
53     def __init__(
54             self, project, version_slug, environment=None,
55             verbose_name=None, version_type=None, **kwargs
56     ):
57         self.default_branch = project.default_branch
58         self.project = project
59         self.name = project.name
60         self.repo_url = project.clean_repo
61         self.working_dir = project.checkout_path(version_slug)
62         # required for External versions
63         self.verbose_name = verbose_name
64         self.version_type = version_type
65 
66         from readthedocs.doc_builder.environments import LocalEnvironment
67         self.environment = environment or LocalEnvironment(project)
68 
69         # Update the env variables with the proper VCS env variables
70         self.environment.environment.update(self.env)
71 
72     def check_working_dir(self):
73         if not os.path.exists(self.working_dir):
74             os.makedirs(self.working_dir)
75 
76     def make_clean_working_dir(self):
77         """Ensures that the working dir exists and is empty."""
78         shutil.rmtree(self.working_dir, ignore_errors=True)
79         self.check_working_dir()
80 
81     @property
82     def env(self):
83         environment = os.environ.copy()
84 
85         # TODO: kind of a hack
86         del environment['PATH']
87 
88         return environment
89 
90     def update(self):
91         """
92         Update a local copy of the repository in self.working_dir.
93 
94         If self.working_dir is already a valid local copy of the repository,
95         update the repository, else create a new local copy of the repository.
96         """
97         self.check_working_dir()
98 
99     def run(self, *cmd, **kwargs):
100         kwargs.update({
101             'cwd': self.working_dir,
102             'shell': False,
103         })
104 
105         build_cmd = self.environment.run(*cmd, **kwargs)
106         # Return a tuple to keep compatibility
107         return (build_cmd.exit_code, build_cmd.output, build_cmd.error)
108 
109     # =========================================================================
110     # Tag / Branch related methods
111     # These methods only apply if supports_tags = True and/or
112     # support_branches = True
113     # =========================================================================
114 
115     @property
116     def tags(self):
117         """
118         Returns a list of VCSVersion objects.
119 
120         See VCSVersion for more information.
121         """
122         raise NotImplementedError
123 
124     @property
125     def branches(self):
126         """
127         Returns a list of VCSVersion objects.
128 
129         See VCSVersion for more information.
130         """
131         raise NotImplementedError
132 
133     @property
134     def commit(self):
135         """Returns a string representing the current commit."""
136         raise NotImplementedError
137 
138     def checkout(self, identifier=None):
139         """
140         Set the state to the given identifier.
141 
142         If identifier is None, checkout to the latest revision.
143 
144         The type and format of identifier may change from VCS to VCS, so each
145         backend is responsible to understand it's identifiers.
146         """
147         self.check_working_dir()
148 
149     def update_submodules(self, config):
150         """
151         Update the submodules of the current checkout.
152 
153         :type config: readthedocs.config.BuildConfigBase
154         """
155         raise NotImplementedError
156
```
--- 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/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py
--- a/readthedocs/vcs_support/base.py
+++ b/readthedocs/vcs_support/base.py
@@ -1,10 +1,11 @@
-# -*- coding: utf-8 -*-
-
 """Base classes for VCS backends."""
 import logging
 import os
 import shutil
 
+from readthedocs.doc_builder.exceptions import BuildEnvironmentWarning
+from readthedocs.projects.exceptions import RepositoryError
+
 
 log = logging.getLogger(__name__)
 
@@ -102,7 +103,13 @@
             'shell': False,
         })
 
-        build_cmd = self.environment.run(*cmd, **kwargs)
+        try:
+            build_cmd = self.environment.run(*cmd, **kwargs)
+        except BuildEnvironmentWarning as e:
+            # Re raise as RepositoryError,
+            # so isn't logged as ERROR.
+            raise RepositoryError(str(e))
+
         # Return a tuple to keep compatibility
         return (build_cmd.exit_code, build_cmd.output, build_cmd.error)

{"golden_diff": "diff --git a/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py\n--- a/readthedocs/vcs_support/base.py\n+++ b/readthedocs/vcs_support/base.py\n@@ -1,10 +1,11 @@\n-# -*- coding: utf-8 -*-\n-\n \"\"\"Base classes for VCS backends.\"\"\"\n import logging\n import os\n import shutil\n \n+from readthedocs.doc_builder.exceptions import BuildEnvironmentWarning\n+from readthedocs.projects.exceptions import RepositoryError\n+\n \n log = logging.getLogger(__name__)\n \n@@ -102,7 +103,13 @@\n             'shell': False,\n         })\n \n-        build_cmd = self.environment.run(*cmd, **kwargs)\n+        try:\n+            build_cmd = self.environment.run(*cmd, **kwargs)\n+        except BuildEnvironmentWarning as e:\n+            # Re raise as RepositoryError,\n+            # so isn't logged as ERROR.\n+            raise RepositoryError(str(e))\n+\n         # Return a tuple to keep compatibility\n         return (build_cmd.exit_code, build_cmd.output, build_cmd.error)\n", "issue": "Cleanup exception that are not logged as error\nAfter #4495 got merged @agjohnson suggested to have an attribute in the Exception class and check for that attribute before log the exception, instead of defining a list for the warning exceptions as I did at:\r\n\r\nhttps://github.com/rtfd/readthedocs.org/pull/4495/files#diff-ca52b098301dd315a834b3556ab9a7d5R424\r\n\r\nAlso, there are more exceptions that have to treat in the same way: `ProjectConfigurationError` for example.\r\n\r\nhttps://sentry.io/read-the-docs/readthedocs-org/issues/668248681/\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Base classes for VCS backends.\"\"\"\nimport logging\nimport os\nimport shutil\n\n\nlog = logging.getLogger(__name__)\n\n\nclass VCSVersion:\n\n    \"\"\"\n    Represents a Version (tag or branch) in a VCS.\n\n    This class should only be instantiated in BaseVCS subclasses.\n\n    It can act as a context manager to temporarily switch to this tag (eg to\n    build docs for this tag).\n    \"\"\"\n\n    def __init__(self, repository, identifier, verbose_name):\n        self.repository = repository\n        self.identifier = identifier\n        self.verbose_name = verbose_name\n\n    def __repr__(self):\n        return '<VCSVersion: {}:{}'.format(\n            self.repository.repo_url,\n            self.verbose_name,\n        )\n\n\nclass BaseVCS:\n\n    \"\"\"\n    Base for VCS Classes.\n\n    VCS commands are ran inside a ``LocalEnvironment``.\n    \"\"\"\n\n    supports_tags = False  # Whether this VCS supports tags or not.\n    supports_branches = False  # Whether this VCS supports branches or not.\n    supports_submodules = False\n\n    # =========================================================================\n    # General methods\n    # =========================================================================\n\n    # Defining a base API, so we'll have unused args\n    # pylint: disable=unused-argument\n    def __init__(\n            self, project, version_slug, environment=None,\n            verbose_name=None, version_type=None, **kwargs\n    ):\n        self.default_branch = project.default_branch\n        self.project = project\n        self.name = project.name\n        self.repo_url = project.clean_repo\n        self.working_dir = project.checkout_path(version_slug)\n        # required for External versions\n        self.verbose_name = verbose_name\n        self.version_type = version_type\n\n        from readthedocs.doc_builder.environments import LocalEnvironment\n        self.environment = environment or LocalEnvironment(project)\n\n        # Update the env variables with the proper VCS env variables\n        self.environment.environment.update(self.env)\n\n    def check_working_dir(self):\n        if not os.path.exists(self.working_dir):\n            os.makedirs(self.working_dir)\n\n    def make_clean_working_dir(self):\n        \"\"\"Ensures that the working dir exists and is empty.\"\"\"\n        shutil.rmtree(self.working_dir, ignore_errors=True)\n        self.check_working_dir()\n\n    @property\n    def env(self):\n        environment = os.environ.copy()\n\n        # TODO: kind of a hack\n        del environment['PATH']\n\n        return environment\n\n    def update(self):\n        \"\"\"\n        Update a local copy of the repository in self.working_dir.\n\n        If self.working_dir is already a valid local copy of the repository,\n        update the repository, else create a new local copy of the repository.\n        \"\"\"\n        self.check_working_dir()\n\n    def run(self, *cmd, **kwargs):\n        kwargs.update({\n            'cwd': self.working_dir,\n            'shell': False,\n        })\n\n        build_cmd = self.environment.run(*cmd, **kwargs)\n        # Return a tuple to keep compatibility\n        return (build_cmd.exit_code, build_cmd.output, build_cmd.error)\n\n    # =========================================================================\n    # Tag / Branch related methods\n    # These methods only apply if supports_tags = True and/or\n    # support_branches = True\n    # =========================================================================\n\n    @property\n    def tags(self):\n        \"\"\"\n        Returns a list of VCSVersion objects.\n\n        See VCSVersion for more information.\n        \"\"\"\n        raise NotImplementedError\n\n    @property\n    def branches(self):\n        \"\"\"\n        Returns a list of VCSVersion objects.\n\n        See VCSVersion for more information.\n        \"\"\"\n        raise NotImplementedError\n\n    @property\n    def commit(self):\n        \"\"\"Returns a string representing the current commit.\"\"\"\n        raise NotImplementedError\n\n    def checkout(self, identifier=None):\n        \"\"\"\n        Set the state to the given identifier.\n\n        If identifier is None, checkout to the latest revision.\n\n        The type and format of identifier may change from VCS to VCS, so each\n        backend is responsible to understand it's identifiers.\n        \"\"\"\n        self.check_working_dir()\n\n    def update_submodules(self, config):\n        \"\"\"\n        Update the submodules of the current checkout.\n\n        :type config: readthedocs.config.BuildConfigBase\n        \"\"\"\n        raise NotImplementedError\n", "path": "readthedocs/vcs_support/base.py"}], "after_files": [{"content": "\"\"\"Base classes for VCS backends.\"\"\"\nimport logging\nimport os\nimport shutil\n\nfrom readthedocs.doc_builder.exceptions import BuildEnvironmentWarning\nfrom readthedocs.projects.exceptions import RepositoryError\n\n\nlog = logging.getLogger(__name__)\n\n\nclass VCSVersion:\n\n    \"\"\"\n    Represents a Version (tag or branch) in a VCS.\n\n    This class should only be instantiated in BaseVCS subclasses.\n\n    It can act as a context manager to temporarily switch to this tag (eg to\n    build docs for this tag).\n    \"\"\"\n\n    def __init__(self, repository, identifier, verbose_name):\n        self.repository = repository\n        self.identifier = identifier\n        self.verbose_name = verbose_name\n\n    def __repr__(self):\n        return '<VCSVersion: {}:{}'.format(\n            self.repository.repo_url,\n            self.verbose_name,\n        )\n\n\nclass BaseVCS:\n\n    \"\"\"\n    Base for VCS Classes.\n\n    VCS commands are ran inside a ``LocalEnvironment``.\n    \"\"\"\n\n    supports_tags = False  # Whether this VCS supports tags or not.\n    supports_branches = False  # Whether this VCS supports branches or not.\n    supports_submodules = False\n\n    # =========================================================================\n    # General methods\n    # =========================================================================\n\n    # Defining a base API, so we'll have unused args\n    # pylint: disable=unused-argument\n    def __init__(\n            self, project, version_slug, environment=None,\n            verbose_name=None, version_type=None, **kwargs\n    ):\n        self.default_branch = project.default_branch\n        self.project = project\n        self.name = project.name\n        self.repo_url = project.clean_repo\n        self.working_dir = project.checkout_path(version_slug)\n        # required for External versions\n        self.verbose_name = verbose_name\n        self.version_type = version_type\n\n        from readthedocs.doc_builder.environments import LocalEnvironment\n        self.environment = environment or LocalEnvironment(project)\n\n        # Update the env variables with the proper VCS env variables\n        self.environment.environment.update(self.env)\n\n    def check_working_dir(self):\n        if not os.path.exists(self.working_dir):\n            os.makedirs(self.working_dir)\n\n    def make_clean_working_dir(self):\n        \"\"\"Ensures that the working dir exists and is empty.\"\"\"\n        shutil.rmtree(self.working_dir, ignore_errors=True)\n        self.check_working_dir()\n\n    @property\n    def env(self):\n        environment = os.environ.copy()\n\n        # TODO: kind of a hack\n        del environment['PATH']\n\n        return environment\n\n    def update(self):\n        \"\"\"\n        Update a local copy of the repository in self.working_dir.\n\n        If self.working_dir is already a valid local copy of the repository,\n        update the repository, else create a new local copy of the repository.\n        \"\"\"\n        self.check_working_dir()\n\n    def run(self, *cmd, **kwargs):\n        kwargs.update({\n            'cwd': self.working_dir,\n            'shell': False,\n        })\n\n        try:\n            build_cmd = self.environment.run(*cmd, **kwargs)\n        except BuildEnvironmentWarning as e:\n            # Re raise as RepositoryError,\n            # so isn't logged as ERROR.\n            raise RepositoryError(str(e))\n\n        # Return a tuple to keep compatibility\n        return (build_cmd.exit_code, build_cmd.output, build_cmd.error)\n\n    # =========================================================================\n    # Tag / Branch related methods\n    # These methods only apply if supports_tags = True and/or\n    # support_branches = True\n    # =========================================================================\n\n    @property\n    def tags(self):\n        \"\"\"\n        Returns a list of VCSVersion objects.\n\n        See VCSVersion for more information.\n        \"\"\"\n        raise NotImplementedError\n\n    @property\n    def branches(self):\n        \"\"\"\n        Returns a list of VCSVersion objects.\n\n        See VCSVersion for more information.\n        \"\"\"\n        raise NotImplementedError\n\n    @property\n    def commit(self):\n        \"\"\"Returns a string representing the current commit.\"\"\"\n        raise NotImplementedError\n\n    def checkout(self, identifier=None):\n        \"\"\"\n        Set the state to the given identifier.\n\n        If identifier is None, checkout to the latest revision.\n\n        The type and format of identifier may change from VCS to VCS, so each\n        backend is responsible to understand it's identifiers.\n        \"\"\"\n        self.check_working_dir()\n\n    def update_submodules(self, config):\n        \"\"\"\n        Update the submodules of the current checkout.\n\n        :type config: readthedocs.config.BuildConfigBase\n        \"\"\"\n        raise NotImplementedError\n", "path": "readthedocs/vcs_support/base.py"}]}