|
6 | 6 | from typing import Any, Union, List
|
7 | 7 | from jedi import Interpreter, Script
|
8 | 8 | from jedi.api.classes import Completion, Signature
|
| 9 | +from importlib.metadata import version |
| 10 | +import sys |
| 11 | +import warnings |
9 | 12 |
|
10 | 13 |
|
11 | 14 | class Mode(Enum):
|
@@ -36,6 +39,13 @@ def __str__(self) -> str:
|
36 | 39 | }
|
37 | 40 |
|
38 | 41 |
|
| 42 | +""" |
| 43 | +For Python 3.9 and 3.10, there is a bug in recursion which can result in a segfault. Lowering this |
| 44 | +limit to 2000 or less seems to mitigate it. |
| 45 | +""" |
| 46 | +MAX_RECURSION_LIMIT = 2000 |
| 47 | + |
| 48 | + |
39 | 49 | def wrap_python(txt: str) -> str:
|
40 | 50 | """ Wraps a string in a Python fenced codeblock for markdown
|
41 | 51 |
|
@@ -80,6 +90,8 @@ def __init__(self):
|
80 | 90 | except ImportError:
|
81 | 91 | self.__can_jedi = False
|
82 | 92 | self.mode = Mode.OFF
|
| 93 | + self.recursion_limit_already_warned = False |
| 94 | + self.check_recursion_limit(True) |
83 | 95 |
|
84 | 96 | @property
|
85 | 97 | def mode(self) -> Mode:
|
@@ -135,6 +147,7 @@ def do_completion(
|
135 | 147 | Modeled after Jedi language server
|
136 | 148 | https://github.com/pappasam/jedi-language-server/blob/main/jedi_language_server/server.py#L189
|
137 | 149 | """
|
| 150 | + self.check_recursion_limit() |
138 | 151 | if not self._versions[uri] == version:
|
139 | 152 | # if you aren't the newest completion, you get nothing, quickly
|
140 | 153 | return []
|
@@ -253,3 +266,27 @@ def do_hover(
|
253 | 266 | hoverstring += '\n---\n' + wrap_plaintext(raw_docstring)
|
254 | 267 |
|
255 | 268 | return hoverstring.strip()
|
| 269 | + |
| 270 | + def check_recursion_limit(self, suppress_warning: bool = False) -> None: |
| 271 | + """ |
| 272 | + Tests for python+jedi+numpy versions that are susceptible to a RecursionError/segfault issue, and lowers |
| 273 | + the recursion limit, warning if the limit is raised externally. |
| 274 | + """ |
| 275 | + if sys.version_info < (3, 9) or sys.version_info >= (3, 11): |
| 276 | + return |
| 277 | + |
| 278 | + if sys.getrecursionlimit() <= MAX_RECURSION_LIMIT: |
| 279 | + return |
| 280 | + |
| 281 | + sys.setrecursionlimit(MAX_RECURSION_LIMIT) |
| 282 | + |
| 283 | + # Log a warning if the user (or some user code) seems to have tried to raise the limit again after we lowered it. |
| 284 | + # This is not a fool-proof way to keep the limit down, and isn't meant to be, only to guard against the primary |
| 285 | + # way we've seen to cause this issue. |
| 286 | + if not suppress_warning and not self.recursion_limit_already_warned: |
| 287 | + self.recursion_limit_already_warned = True |
| 288 | + warnings.warn(f"""Recursion limit has been set to {MAX_RECURSION_LIMIT} to avoid a known segfault in Python 3.9 and 3.10 |
| 289 | +related to RecursionErrors. This limit will be set to {MAX_RECURSION_LIMIT} whenever autocomplete takes place |
| 290 | +to avoid this, because the jedi library sets this to 3000, above the safe limit. Disabling autocomplete |
| 291 | +will prevent this check, as it will also prevent jedi from raising the limit. |
| 292 | +See https://github.com/deephaven/deephaven-core/issues/5878 for more information.""") |
0 commit comments