We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Replace_more() AssertionError After Refreshing Comment
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
Using the replace_more() function on a comment's replies after the comment has been refreshed raises an AssertionError. This only happens on certain submissions and I'm not sure why (the code below is an example of a submission that fails). Also, the AssertionError is not raised if the refresh() function is not called on the comment.

**To Reproduce**
Run this code after replacing your client_id, client_secret, and user_agent.

```
import praw

reddit = praw.Reddit(
    client_id="YOUR-CLIENT-ID",
    client_secret="YOUR-CLIENT-SECRET",
    user_agent="YOUR-USER-AGENT",
)

submission = reddit.submission(id="6aeian")

submission.comment_sort = "confidence"
submission.comments.replace_more()
comments = submission.comments.list()

for comment in comments:
    comment.reply_sort = "confidence"
    comment.refresh()

    # Errors on line below
    comment.replies.replace_more()
```

**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
I expected this to run without raising any exceptions.

**Code/Logs**
<!-- include your code, without the Reddit() initialization, so as to not leak private credentials. -->
```
Traceback (most recent call last):
  File "/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/test_praw.py", line 19, in <module>
    comment.replies.replace_more()
  File "/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/Reddit-Bot/lib/python3.6/site-packages/praw/models/comment_forest.py", line 177, in replace_more
    self._insert_comment(comment)
  File "/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/Reddit-Bot/lib/python3.6/site-packages/praw/models/comment_forest.py", line 69, in _insert_comment
    assert comment.name not in self._submission._comments_by_id
AssertionError
```
**System Info**
 - OS: WSL running Ubuntu 18.04.3 LTS
 - Python: 3.6.9
 - PRAW Version: 6.4.0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `praw/models/comment_forest.py`
Content:
```
1 """Provide CommentForest for Submission comments."""
2 from heapq import heappop, heappush
3 from typing import List, Optional, TypeVar, Union
4 
5 from .reddit.more import MoreComments
6 
7 Comment = TypeVar("Comment")
8 Submission = TypeVar("Submission")
9 
10 
11 class CommentForest:
12     """A forest of comments starts with multiple top-level comments.
13 
14     Each of these comments can be a tree of replies.
15 
16     """
17 
18     @staticmethod
19     def _gather_more_comments(tree, parent_tree=None):
20         """Return a list of MoreComments objects obtained from tree."""
21         more_comments = []
22         queue = [(None, x) for x in tree]
23         while queue:
24             parent, comment = queue.pop(0)
25             if isinstance(comment, MoreComments):
26                 heappush(more_comments, comment)
27                 if parent:
28                     comment._remove_from = parent.replies._comments
29                 else:
30                     comment._remove_from = parent_tree or tree
31             else:
32                 for item in comment.replies:
33                     queue.append((comment, item))
34         return more_comments
35 
36     def __getitem__(self, index: int):
37         """Return the comment at position ``index`` in the list.
38 
39         This method is to be used like an array access, such as:
40 
41         .. code-block:: python
42 
43            first_comment = submission.comments[0]
44 
45         Alternatively, the presence of this method enables one to iterate over
46         all top_level comments, like so:
47 
48         .. code-block:: python
49 
50            for comment in submission.comments:
51                print(comment.body)
52 
53         """
54         return self._comments[index]
55 
56     def __init__(
57         self, submission: Submission, comments: Optional[List[Comment]] = None
58     ):
59         """Initialize a CommentForest instance.
60 
61         :param submission: An instance of :class:`~.Subreddit` that is the
62             parent of the comments.
63         :param comments: Initialize the Forest with a list of comments
64             (default: None).
65 
66         """
67         self._comments = comments
68         self._submission = submission
69 
70     def __len__(self) -> int:
71         """Return the number of top-level comments in the forest."""
72         return len(self._comments)
73 
74     def _insert_comment(self, comment):
75         assert comment.name not in self._submission._comments_by_id
76         comment.submission = self._submission
77         if isinstance(comment, MoreComments) or comment.is_root:
78             self._comments.append(comment)
79         else:
80             assert comment.parent_id in self._submission._comments_by_id
81             parent = self._submission._comments_by_id[comment.parent_id]
82             parent.replies._comments.append(comment)
83 
84     def _update(self, comments):
85         self._comments = comments
86         for comment in comments:
87             comment.submission = self._submission
88 
89     def list(self) -> Union[Comment, MoreComments]:
90         """Return a flattened list of all Comments.
91 
92         This list may contain :class:`.MoreComments` instances if
93         :meth:`.replace_more` was not called first.
94 
95         """
96         comments = []
97         queue = list(self)
98         while queue:
99             comment = queue.pop(0)
100             comments.append(comment)
101             if not isinstance(comment, MoreComments):
102                 queue.extend(comment.replies)
103         return comments
104 
105     def replace_more(
106         self, limit: int = 32, threshold: int = 0
107     ) -> List[MoreComments]:
108         """Update the comment forest by resolving instances of MoreComments.
109 
110         :param limit: The maximum number of :class:`.MoreComments` instances to
111             replace. Each replacement requires 1 API request. Set to ``None``
112             to have no limit, or to ``0`` to remove all :class:`.MoreComments`
113             instances without additional requests (default: 32).
114         :param threshold: The minimum number of children comments a
115             :class:`.MoreComments` instance must have in order to be
116             replaced. :class:`.MoreComments` instances that represent "continue
117             this thread" links unfortunately appear to have 0
118             children. (default: 0).
119 
120         :returns: A list of :class:`.MoreComments` instances that were not
121             replaced.
122 
123         For example, to replace up to 32 :class:`.MoreComments` instances of a
124         submission try:
125 
126         .. code-block:: python
127 
128            submission = reddit.submission('3hahrw')
129            submission.comments.replace_more()
130 
131         Alternatively, to replace :class:`.MoreComments` instances within the
132         replies of a single comment try:
133 
134         .. code-block:: python
135 
136            comment = reddit.comment('d8r4im1')
137            comment.refresh()
138            comment.replies.replace_more()
139 
140         .. note:: This method can take a long time as each replacement will
141                   discover at most 20 new :class:`.Comment` or
142                   :class:`.MoreComments` instances. As a result, consider
143                   looping and handling exceptions until the method returns
144                   successfully. For example:
145 
146                   .. code-block:: python
147 
148                      while True:
149                          try:
150                              submission.comments.replace_more()
151                              break
152                          except PossibleExceptions:
153                              print('Handling replace_more exception')
154                              sleep(1)
155 
156         """
157         remaining = limit
158         more_comments = self._gather_more_comments(self._comments)
159         skipped = []
160 
161         # Fetch largest more_comments until reaching the limit or the threshold
162         while more_comments:
163             item = heappop(more_comments)
164             if (
165                 remaining is not None
166                 and remaining <= 0
167                 or item.count < threshold
168             ):
169                 skipped.append(item)
170                 item._remove_from.remove(item)
171                 continue
172 
173             new_comments = item.comments(update=False)
174             if remaining is not None:
175                 remaining -= 1
176 
177             # Add new MoreComment objects to the heap of more_comments
178             for more in self._gather_more_comments(
179                 new_comments, self._comments
180             ):
181                 more.submission = self._submission
182                 heappush(more_comments, more)
183             # Insert all items into the tree
184             for comment in new_comments:
185                 self._insert_comment(comment)
186 
187             # Remove from forest
188             item._remove_from.remove(item)
189 
190         return more_comments + skipped
191
```

Path: `praw/exceptions.py`
Content:
```
1 """PRAW exception classes.
2 
3 Includes two main exceptions: :class:`.APIException` for when something goes
4 wrong on the server side, and :class:`.ClientException` when something goes
5 wrong on the client side. Both of these classes extend :class:`.PRAWException`.
6 
7 """
8 from typing import Optional
9 
10 
11 class PRAWException(Exception):
12     """The base PRAW Exception that all other exception classes extend."""
13 
14 
15 class APIException(PRAWException):
16     """Indicate exception that involve responses from Reddit's API."""
17 
18     def __init__(self, error_type: str, message: str, field: Optional[str]):
19         """Initialize an instance of APIException.
20 
21         :param error_type: The error type set on Reddit's end.
22         :param message: The associated message for the error.
23         :param field: The input field associated with the error if available.
24         """
25         error_str = "{}: '{}'".format(error_type, message)
26         if field:
27             error_str += " on field '{}'".format(field)
28 
29         super().__init__(error_str)
30         self.error_type = error_type
31         self.message = message
32         self.field = field
33 
34 
35 class ClientException(PRAWException):
36     """Indicate exceptions that don't involve interaction with Reddit's API."""
37 
38 
39 class WebSocketException(ClientException):
40     """Indicate exceptions caused by use of WebSockets."""
41 
42     def __init__(self, message: str, exception: Exception):
43         """Initialize a WebSocketException.
44 
45         :param message: The exception message.
46         :param exception: The exception thrown by the websocket library.
47         """
48         super().__init__(message)
49         self.original_exception = exception
50
```
--- 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/praw/exceptions.py b/praw/exceptions.py
--- a/praw/exceptions.py
+++ b/praw/exceptions.py
@@ -36,10 +36,21 @@
     """Indicate exceptions that don't involve interaction with Reddit's API."""
 
 
+class DuplicateReplaceException(ClientException):
+    """Indicate exceptions that involve the replacement of MoreComments."""
+
+    def __init__(self):
+        """Instantize the class."""
+        super().__init__(
+            "A duplicate comment has been detected. Are you attempting to call"
+            " ``replace_more_comments`` more than once?"
+        )
+
+
 class WebSocketException(ClientException):
     """Indicate exceptions caused by use of WebSockets."""
 
-    def __init__(self, message, exception):
+    def __init__(self, message: str, exception: Exception):
         """Initialize a WebSocketException.
 
         :param message: The exception message.
diff --git a/praw/models/comment_forest.py b/praw/models/comment_forest.py
--- a/praw/models/comment_forest.py
+++ b/praw/models/comment_forest.py
@@ -3,6 +3,7 @@
 from typing import List, Optional, TypeVar, Union
 
 from .reddit.more import MoreComments
+from ..exceptions import DuplicateReplaceException
 
 Comment = TypeVar("Comment")
 Submission = TypeVar("Submission")
@@ -72,12 +73,16 @@
         return len(self._comments)
 
     def _insert_comment(self, comment):
-        assert comment.name not in self._submission._comments_by_id
+        if comment.name in self._submission._comments_by_id:
+            raise DuplicateReplaceException
         comment.submission = self._submission
         if isinstance(comment, MoreComments) or comment.is_root:
             self._comments.append(comment)
         else:
-            assert comment.parent_id in self._submission._comments_by_id
+            assert comment.parent_id in self._submission._comments_by_id, (
+                "PRAW Error occured. Please file a bug report and include "
+                "the code that caused the error."
+            )
             parent = self._submission._comments_by_id[comment.parent_id]
             parent.replies._comments.append(comment)

{"golden_diff": "diff --git a/praw/exceptions.py b/praw/exceptions.py\n--- a/praw/exceptions.py\n+++ b/praw/exceptions.py\n@@ -36,10 +36,21 @@\n     \"\"\"Indicate exceptions that don't involve interaction with Reddit's API.\"\"\"\n \n \n+class DuplicateReplaceException(ClientException):\n+    \"\"\"Indicate exceptions that involve the replacement of MoreComments.\"\"\"\n+\n+    def __init__(self):\n+        \"\"\"Instantize the class.\"\"\"\n+        super().__init__(\n+            \"A duplicate comment has been detected. Are you attempting to call\"\n+            \" ``replace_more_comments`` more than once?\"\n+        )\n+\n+\n class WebSocketException(ClientException):\n     \"\"\"Indicate exceptions caused by use of WebSockets.\"\"\"\n \n-    def __init__(self, message, exception):\n+    def __init__(self, message: str, exception: Exception):\n         \"\"\"Initialize a WebSocketException.\n \n         :param message: The exception message.\ndiff --git a/praw/models/comment_forest.py b/praw/models/comment_forest.py\n--- a/praw/models/comment_forest.py\n+++ b/praw/models/comment_forest.py\n@@ -3,6 +3,7 @@\n from typing import List, Optional, TypeVar, Union\n \n from .reddit.more import MoreComments\n+from ..exceptions import DuplicateReplaceException\n \n Comment = TypeVar(\"Comment\")\n Submission = TypeVar(\"Submission\")\n@@ -72,12 +73,16 @@\n         return len(self._comments)\n \n     def _insert_comment(self, comment):\n-        assert comment.name not in self._submission._comments_by_id\n+        if comment.name in self._submission._comments_by_id:\n+            raise DuplicateReplaceException\n         comment.submission = self._submission\n         if isinstance(comment, MoreComments) or comment.is_root:\n             self._comments.append(comment)\n         else:\n-            assert comment.parent_id in self._submission._comments_by_id\n+            assert comment.parent_id in self._submission._comments_by_id, (\n+                \"PRAW Error occured. Please file a bug report and include \"\n+                \"the code that caused the error.\"\n+            )\n             parent = self._submission._comments_by_id[comment.parent_id]\n             parent.replies._comments.append(comment)\n", "issue": "Replace_more() AssertionError After Refreshing Comment\n**Describe the bug**\r\n<!-- A clear and concise description of what the bug is. -->\r\nUsing the replace_more() function on a comment's replies after the comment has been refreshed raises an AssertionError. This only happens on certain submissions and I'm not sure why (the code below is an example of a submission that fails). Also, the AssertionError is not raised if the refresh() function is not called on the comment.\r\n\r\n**To Reproduce**\r\nRun this code after replacing your client_id, client_secret, and user_agent.\r\n\r\n```\r\nimport praw\r\n\r\nreddit = praw.Reddit(\r\n    client_id=\"YOUR-CLIENT-ID\",\r\n    client_secret=\"YOUR-CLIENT-SECRET\",\r\n    user_agent=\"YOUR-USER-AGENT\",\r\n)\r\n\r\nsubmission = reddit.submission(id=\"6aeian\")\r\n\r\nsubmission.comment_sort = \"confidence\"\r\nsubmission.comments.replace_more()\r\ncomments = submission.comments.list()\r\n\r\nfor comment in comments:\r\n    comment.reply_sort = \"confidence\"\r\n    comment.refresh()\r\n\r\n    # Errors on line below\r\n    comment.replies.replace_more()\r\n```\r\n\r\n**Expected behavior**\r\n<!-- A clear and concise description of what you expected to happen. -->\r\nI expected this to run without raising any exceptions.\r\n\r\n**Code/Logs**\r\n<!-- include your code, without the Reddit() initialization, so as to not leak private credentials. -->\r\n```\r\nTraceback (most recent call last):\r\n  File \"/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/test_praw.py\", line 19, in <module>\r\n    comment.replies.replace_more()\r\n  File \"/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/Reddit-Bot/lib/python3.6/site-packages/praw/models/comment_forest.py\", line 177, in replace_more\r\n    self._insert_comment(comment)\r\n  File \"/mnt/c/Users/<NAME>/Documents/GitHub/Reddit-Bot/Reddit-Bot/lib/python3.6/site-packages/praw/models/comment_forest.py\", line 69, in _insert_comment\r\n    assert comment.name not in self._submission._comments_by_id\r\nAssertionError\r\n```\r\n**System Info**\r\n - OS: WSL running Ubuntu 18.04.3 LTS\r\n - Python: 3.6.9\r\n - PRAW Version: 6.4.0\r\n\n", "before_files": [{"content": "\"\"\"Provide CommentForest for Submission comments.\"\"\"\nfrom heapq import heappop, heappush\nfrom typing import List, Optional, TypeVar, Union\n\nfrom .reddit.more import MoreComments\n\nComment = TypeVar(\"Comment\")\nSubmission = TypeVar(\"Submission\")\n\n\nclass CommentForest:\n    \"\"\"A forest of comments starts with multiple top-level comments.\n\n    Each of these comments can be a tree of replies.\n\n    \"\"\"\n\n    @staticmethod\n    def _gather_more_comments(tree, parent_tree=None):\n        \"\"\"Return a list of MoreComments objects obtained from tree.\"\"\"\n        more_comments = []\n        queue = [(None, x) for x in tree]\n        while queue:\n            parent, comment = queue.pop(0)\n            if isinstance(comment, MoreComments):\n                heappush(more_comments, comment)\n                if parent:\n                    comment._remove_from = parent.replies._comments\n                else:\n                    comment._remove_from = parent_tree or tree\n            else:\n                for item in comment.replies:\n                    queue.append((comment, item))\n        return more_comments\n\n    def __getitem__(self, index: int):\n        \"\"\"Return the comment at position ``index`` in the list.\n\n        This method is to be used like an array access, such as:\n\n        .. code-block:: python\n\n           first_comment = submission.comments[0]\n\n        Alternatively, the presence of this method enables one to iterate over\n        all top_level comments, like so:\n\n        .. code-block:: python\n\n           for comment in submission.comments:\n               print(comment.body)\n\n        \"\"\"\n        return self._comments[index]\n\n    def __init__(\n        self, submission: Submission, comments: Optional[List[Comment]] = None\n    ):\n        \"\"\"Initialize a CommentForest instance.\n\n        :param submission: An instance of :class:`~.Subreddit` that is the\n            parent of the comments.\n        :param comments: Initialize the Forest with a list of comments\n            (default: None).\n\n        \"\"\"\n        self._comments = comments\n        self._submission = submission\n\n    def __len__(self) -> int:\n        \"\"\"Return the number of top-level comments in the forest.\"\"\"\n        return len(self._comments)\n\n    def _insert_comment(self, comment):\n        assert comment.name not in self._submission._comments_by_id\n        comment.submission = self._submission\n        if isinstance(comment, MoreComments) or comment.is_root:\n            self._comments.append(comment)\n        else:\n            assert comment.parent_id in self._submission._comments_by_id\n            parent = self._submission._comments_by_id[comment.parent_id]\n            parent.replies._comments.append(comment)\n\n    def _update(self, comments):\n        self._comments = comments\n        for comment in comments:\n            comment.submission = self._submission\n\n    def list(self) -> Union[Comment, MoreComments]:\n        \"\"\"Return a flattened list of all Comments.\n\n        This list may contain :class:`.MoreComments` instances if\n        :meth:`.replace_more` was not called first.\n\n        \"\"\"\n        comments = []\n        queue = list(self)\n        while queue:\n            comment = queue.pop(0)\n            comments.append(comment)\n            if not isinstance(comment, MoreComments):\n                queue.extend(comment.replies)\n        return comments\n\n    def replace_more(\n        self, limit: int = 32, threshold: int = 0\n    ) -> List[MoreComments]:\n        \"\"\"Update the comment forest by resolving instances of MoreComments.\n\n        :param limit: The maximum number of :class:`.MoreComments` instances to\n            replace. Each replacement requires 1 API request. Set to ``None``\n            to have no limit, or to ``0`` to remove all :class:`.MoreComments`\n            instances without additional requests (default: 32).\n        :param threshold: The minimum number of children comments a\n            :class:`.MoreComments` instance must have in order to be\n            replaced. :class:`.MoreComments` instances that represent \"continue\n            this thread\" links unfortunately appear to have 0\n            children. (default: 0).\n\n        :returns: A list of :class:`.MoreComments` instances that were not\n            replaced.\n\n        For example, to replace up to 32 :class:`.MoreComments` instances of a\n        submission try:\n\n        .. code-block:: python\n\n           submission = reddit.submission('3hahrw')\n           submission.comments.replace_more()\n\n        Alternatively, to replace :class:`.MoreComments` instances within the\n        replies of a single comment try:\n\n        .. code-block:: python\n\n           comment = reddit.comment('d8r4im1')\n           comment.refresh()\n           comment.replies.replace_more()\n\n        .. note:: This method can take a long time as each replacement will\n                  discover at most 20 new :class:`.Comment` or\n                  :class:`.MoreComments` instances. As a result, consider\n                  looping and handling exceptions until the method returns\n                  successfully. For example:\n\n                  .. code-block:: python\n\n                     while True:\n                         try:\n                             submission.comments.replace_more()\n                             break\n                         except PossibleExceptions:\n                             print('Handling replace_more exception')\n                             sleep(1)\n\n        \"\"\"\n        remaining = limit\n        more_comments = self._gather_more_comments(self._comments)\n        skipped = []\n\n        # Fetch largest more_comments until reaching the limit or the threshold\n        while more_comments:\n            item = heappop(more_comments)\n            if (\n                remaining is not None\n                and remaining <= 0\n                or item.count < threshold\n            ):\n                skipped.append(item)\n                item._remove_from.remove(item)\n                continue\n\n            new_comments = item.comments(update=False)\n            if remaining is not None:\n                remaining -= 1\n\n            # Add new MoreComment objects to the heap of more_comments\n            for more in self._gather_more_comments(\n                new_comments, self._comments\n            ):\n                more.submission = self._submission\n                heappush(more_comments, more)\n            # Insert all items into the tree\n            for comment in new_comments:\n                self._insert_comment(comment)\n\n            # Remove from forest\n            item._remove_from.remove(item)\n\n        return more_comments + skipped\n", "path": "praw/models/comment_forest.py"}, {"content": "\"\"\"PRAW exception classes.\n\nIncludes two main exceptions: :class:`.APIException` for when something goes\nwrong on the server side, and :class:`.ClientException` when something goes\nwrong on the client side. Both of these classes extend :class:`.PRAWException`.\n\n\"\"\"\nfrom typing import Optional\n\n\nclass PRAWException(Exception):\n    \"\"\"The base PRAW Exception that all other exception classes extend.\"\"\"\n\n\nclass APIException(PRAWException):\n    \"\"\"Indicate exception that involve responses from Reddit's API.\"\"\"\n\n    def __init__(self, error_type: str, message: str, field: Optional[str]):\n        \"\"\"Initialize an instance of APIException.\n\n        :param error_type: The error type set on Reddit's end.\n        :param message: The associated message for the error.\n        :param field: The input field associated with the error if available.\n        \"\"\"\n        error_str = \"{}: '{}'\".format(error_type, message)\n        if field:\n            error_str += \" on field '{}'\".format(field)\n\n        super().__init__(error_str)\n        self.error_type = error_type\n        self.message = message\n        self.field = field\n\n\nclass ClientException(PRAWException):\n    \"\"\"Indicate exceptions that don't involve interaction with Reddit's API.\"\"\"\n\n\nclass WebSocketException(ClientException):\n    \"\"\"Indicate exceptions caused by use of WebSockets.\"\"\"\n\n    def __init__(self, message: str, exception: Exception):\n        \"\"\"Initialize a WebSocketException.\n\n        :param message: The exception message.\n        :param exception: The exception thrown by the websocket library.\n        \"\"\"\n        super().__init__(message)\n        self.original_exception = exception\n", "path": "praw/exceptions.py"}], "after_files": [{"content": "\"\"\"Provide CommentForest for Submission comments.\"\"\"\nfrom heapq import heappop, heappush\nfrom typing import List, Optional, TypeVar, Union\n\nfrom .reddit.more import MoreComments\nfrom ..exceptions import DuplicateReplaceException\n\nComment = TypeVar(\"Comment\")\nSubmission = TypeVar(\"Submission\")\n\n\nclass CommentForest:\n    \"\"\"A forest of comments starts with multiple top-level comments.\n\n    Each of these comments can be a tree of replies.\n\n    \"\"\"\n\n    @staticmethod\n    def _gather_more_comments(tree, parent_tree=None):\n        \"\"\"Return a list of MoreComments objects obtained from tree.\"\"\"\n        more_comments = []\n        queue = [(None, x) for x in tree]\n        while queue:\n            parent, comment = queue.pop(0)\n            if isinstance(comment, MoreComments):\n                heappush(more_comments, comment)\n                if parent:\n                    comment._remove_from = parent.replies._comments\n                else:\n                    comment._remove_from = parent_tree or tree\n            else:\n                for item in comment.replies:\n                    queue.append((comment, item))\n        return more_comments\n\n    def __getitem__(self, index: int):\n        \"\"\"Return the comment at position ``index`` in the list.\n\n        This method is to be used like an array access, such as:\n\n        .. code-block:: python\n\n           first_comment = submission.comments[0]\n\n        Alternatively, the presence of this method enables one to iterate over\n        all top_level comments, like so:\n\n        .. code-block:: python\n\n           for comment in submission.comments:\n               print(comment.body)\n\n        \"\"\"\n        return self._comments[index]\n\n    def __init__(\n        self, submission: Submission, comments: Optional[List[Comment]] = None\n    ):\n        \"\"\"Initialize a CommentForest instance.\n\n        :param submission: An instance of :class:`~.Subreddit` that is the\n            parent of the comments.\n        :param comments: Initialize the Forest with a list of comments\n            (default: None).\n\n        \"\"\"\n        self._comments = comments\n        self._submission = submission\n\n    def __len__(self) -> int:\n        \"\"\"Return the number of top-level comments in the forest.\"\"\"\n        return len(self._comments)\n\n    def _insert_comment(self, comment):\n        if comment.name in self._submission._comments_by_id:\n            raise DuplicateReplaceException\n        comment.submission = self._submission\n        if isinstance(comment, MoreComments) or comment.is_root:\n            self._comments.append(comment)\n        else:\n            assert comment.parent_id in self._submission._comments_by_id, (\n                \"PRAW Error occured. Please file a bug report and include \"\n                \"the code that caused the error.\"\n            )\n            parent = self._submission._comments_by_id[comment.parent_id]\n            parent.replies._comments.append(comment)\n\n    def _update(self, comments):\n        self._comments = comments\n        for comment in comments:\n            comment.submission = self._submission\n\n    def list(self) -> Union[Comment, MoreComments]:\n        \"\"\"Return a flattened list of all Comments.\n\n        This list may contain :class:`.MoreComments` instances if\n        :meth:`.replace_more` was not called first.\n\n        \"\"\"\n        comments = []\n        queue = list(self)\n        while queue:\n            comment = queue.pop(0)\n            comments.append(comment)\n            if not isinstance(comment, MoreComments):\n                queue.extend(comment.replies)\n        return comments\n\n    def replace_more(\n        self, limit: int = 32, threshold: int = 0\n    ) -> List[MoreComments]:\n        \"\"\"Update the comment forest by resolving instances of MoreComments.\n\n        :param limit: The maximum number of :class:`.MoreComments` instances to\n            replace. Each replacement requires 1 API request. Set to ``None``\n            to have no limit, or to ``0`` to remove all :class:`.MoreComments`\n            instances without additional requests (default: 32).\n        :param threshold: The minimum number of children comments a\n            :class:`.MoreComments` instance must have in order to be\n            replaced. :class:`.MoreComments` instances that represent \"continue\n            this thread\" links unfortunately appear to have 0\n            children. (default: 0).\n\n        :returns: A list of :class:`.MoreComments` instances that were not\n            replaced.\n\n        For example, to replace up to 32 :class:`.MoreComments` instances of a\n        submission try:\n\n        .. code-block:: python\n\n           submission = reddit.submission('3hahrw')\n           submission.comments.replace_more()\n\n        Alternatively, to replace :class:`.MoreComments` instances within the\n        replies of a single comment try:\n\n        .. code-block:: python\n\n           comment = reddit.comment('d8r4im1')\n           comment.refresh()\n           comment.replies.replace_more()\n\n        .. note:: This method can take a long time as each replacement will\n                  discover at most 20 new :class:`.Comment` or\n                  :class:`.MoreComments` instances. As a result, consider\n                  looping and handling exceptions until the method returns\n                  successfully. For example:\n\n                  .. code-block:: python\n\n                     while True:\n                         try:\n                             submission.comments.replace_more()\n                             break\n                         except PossibleExceptions:\n                             print('Handling replace_more exception')\n                             sleep(1)\n\n        \"\"\"\n        remaining = limit\n        more_comments = self._gather_more_comments(self._comments)\n        skipped = []\n\n        # Fetch largest more_comments until reaching the limit or the threshold\n        while more_comments:\n            item = heappop(more_comments)\n            if (\n                remaining is not None\n                and remaining <= 0\n                or item.count < threshold\n            ):\n                skipped.append(item)\n                item._remove_from.remove(item)\n                continue\n\n            new_comments = item.comments(update=False)\n            if remaining is not None:\n                remaining -= 1\n\n            # Add new MoreComment objects to the heap of more_comments\n            for more in self._gather_more_comments(\n                new_comments, self._comments\n            ):\n                more.submission = self._submission\n                heappush(more_comments, more)\n            # Insert all items into the tree\n            for comment in new_comments:\n                self._insert_comment(comment)\n\n            # Remove from forest\n            item._remove_from.remove(item)\n\n        return more_comments + skipped\n", "path": "praw/models/comment_forest.py"}, {"content": "\"\"\"PRAW exception classes.\n\nIncludes two main exceptions: :class:`.APIException` for when something goes\nwrong on the server side, and :class:`.ClientException` when something goes\nwrong on the client side. Both of these classes extend :class:`.PRAWException`.\n\n\"\"\"\nfrom typing import Optional\n\n\nclass PRAWException(Exception):\n    \"\"\"The base PRAW Exception that all other exception classes extend.\"\"\"\n\n\nclass APIException(PRAWException):\n    \"\"\"Indicate exception that involve responses from Reddit's API.\"\"\"\n\n    def __init__(self, error_type: str, message: str, field: Optional[str]):\n        \"\"\"Initialize an instance of APIException.\n\n        :param error_type: The error type set on Reddit's end.\n        :param message: The associated message for the error.\n        :param field: The input field associated with the error if available.\n        \"\"\"\n        error_str = \"{}: '{}'\".format(error_type, message)\n        if field:\n            error_str += \" on field '{}'\".format(field)\n\n        super().__init__(error_str)\n        self.error_type = error_type\n        self.message = message\n        self.field = field\n\n\nclass ClientException(PRAWException):\n    \"\"\"Indicate exceptions that don't involve interaction with Reddit's API.\"\"\"\n\n\nclass DuplicateReplaceException(ClientException):\n    \"\"\"Indicate exceptions that involve the replacement of MoreComments.\"\"\"\n\n    def __init__(self):\n        \"\"\"Instantize the class.\"\"\"\n        super().__init__(\n            \"A duplicate comment has been detected. Are you attempting to call\"\n            \" ``replace_more_comments`` more than once?\"\n        )\n\n\nclass WebSocketException(ClientException):\n    \"\"\"Indicate exceptions caused by use of WebSockets.\"\"\"\n\n    def __init__(self, message: str, exception: Exception):\n        \"\"\"Initialize a WebSocketException.\n\n        :param message: The exception message.\n        :param exception: The exception thrown by the websocket library.\n        \"\"\"\n        super().__init__(message)\n        self.original_exception = exception\n", "path": "praw/exceptions.py"}]}