aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2025-02-16 00:51:45 +0000
committerAlexander Smirnov <alex@ydb.tech>2025-02-16 00:51:45 +0000
commit436fda2cd94c199f31d890959fbc5e58a509c0a1 (patch)
treea6db297b18588c20ea26490b4d6bcc3dc8c32113 /contrib
parent0fc5672f94482c8d6e6afba9e34db7ed66a6c0fb (diff)
parent802da2736bf00631aa408e495b80d6e125f10a9f (diff)
downloadydb-436fda2cd94c199f31d890959fbc5e58a509c0a1.tar.gz
Merge branch 'rightlib' into merge-libs-250216-0050
Diffstat (limited to 'contrib')
-rw-r--r--contrib/libs/simdjson/.yandex_meta/devtools.licenses.report30
-rw-r--r--contrib/libs/simdjson/.yandex_meta/override.nix4
-rw-r--r--contrib/libs/simdjson/README.md1
-rw-r--r--contrib/libs/simdjson/include/simdjson/icelake/simd.h8
-rw-r--r--contrib/libs/simdjson/include/simdjson/simdjson_version.h4
-rw-r--r--contrib/libs/simdjson/ya.make4
-rw-r--r--contrib/python/ipython/py3/.dist-info/METADATA7
-rw-r--r--contrib/python/ipython/py3/IPython/core/interactiveshell.py6
-rw-r--r--contrib/python/ipython/py3/IPython/core/magics/execution.py16
-rw-r--r--contrib/python/ipython/py3/IPython/core/magics/script.py35
-rw-r--r--contrib/python/ipython/py3/IPython/core/release.py2
-rw-r--r--contrib/python/ipython/py3/IPython/terminal/interactiveshell.py49
-rw-r--r--contrib/python/ipython/py3/IPython/terminal/shortcuts/auto_suggest.py279
-rw-r--r--contrib/python/ipython/py3/IPython/utils/_sysinfo.py2
-rw-r--r--contrib/python/ipython/py3/ya.make2
15 files changed, 392 insertions, 57 deletions
diff --git a/contrib/libs/simdjson/.yandex_meta/devtools.licenses.report b/contrib/libs/simdjson/.yandex_meta/devtools.licenses.report
index 2aad6abcfa..e29fcfdfcb 100644
--- a/contrib/libs/simdjson/.yandex_meta/devtools.licenses.report
+++ b/contrib/libs/simdjson/.yandex_meta/devtools.licenses.report
@@ -42,7 +42,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
Files with this license:
- README.md [235:235]
+ README.md [234:234]
KEEP MIT 0a00f0d66f4f37595306dd8c6a25c63c
BELONGS ya.make
@@ -54,7 +54,7 @@ BELONGS ya.make
Match type : NOTICE
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
- README.md [231:231]
+ README.md [230:230]
SKIP LicenseRef-scancode-unknown-license-reference 0d48e0b09865a98a90db20ea37b36bb8
BELONGS ya.make
@@ -66,7 +66,7 @@ BELONGS ya.make
Match type : INTRO
Links : https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.LICENSE
Files with this license:
- README.md [239:239]
+ README.md [238:238]
KEEP Apache-2.0 13ec3cccf3036f38df47d2051a825972
BELONGS ya.make
@@ -102,7 +102,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.apache.org/licenses/, http://www.apache.org/licenses/LICENSE-2.0, https://spdx.org/licenses/Apache-2.0
Files with this license:
- README.md [215:215]
+ README.md [214:214]
SKIP BSL-1.0 2a9212d785cde4078c2f6803e544de21
BELONGS ya.make
@@ -113,7 +113,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
Files with this license:
- README.md [235:235]
+ README.md [234:234]
KEEP MIT 3e1ede6948a97e7ee3d75e0204a567f3
BELONGS ya.make
@@ -125,7 +125,7 @@ BELONGS ya.make
Match type : TAG
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
- README.md [219:219]
+ README.md [218:218]
SKIP Apache-2.0 500a503129337bb5adf5977ce11879cd
BELONGS ya.make
@@ -137,7 +137,7 @@ BELONGS ya.make
Match type : NOTICE
Links : http://www.apache.org/licenses/, http://www.apache.org/licenses/LICENSE-2.0, https://spdx.org/licenses/Apache-2.0
Files with this license:
- README.md [231:231]
+ README.md [230:230]
SKIP BSL-1.0 77dd56e30840a227692d435b4aecdb95
BELONGS ya.make
@@ -148,11 +148,11 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
Files with this license:
- README.md [235:235]
+ README.md [234:234]
KEEP MIT 7f0bdbc0a0545831259b66259ac6b604
BELONGS ya.make
-FILE_INCLUDE LICENSE-MIT found in files: README.md at line 218
+FILE_INCLUDE LICENSE-MIT found in files: README.md at line 217
License text:
[licensemit]: LICENSE-MIT
Scancode info:
@@ -161,7 +161,7 @@ FILE_INCLUDE LICENSE-MIT found in files: README.md at line 218
Match type : TAG
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
- README.md [218:218]
+ README.md [217:217]
KEEP Apache-2.0 82e76bbc1841bd5886297e795c72bfa5
BELONGS ya.make
@@ -173,7 +173,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.apache.org/licenses/, http://www.apache.org/licenses/LICENSE-2.0, https://spdx.org/licenses/Apache-2.0
Files with this license:
- README.md [231:231]
+ README.md [230:230]
KEEP MIT a6e9f2d79eb73e6e422759b53da6152a
BELONGS ya.make
@@ -208,7 +208,7 @@ BELONGS ya.make
Match type : NOTICE
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
- README.md [237:237]
+ README.md [236:236]
SKIP Apache-2.0 c23a044f4165feb9568f486ca3b30fc8
BELONGS ya.make
@@ -219,7 +219,7 @@ BELONGS ya.make
Match type : NOTICE
Links : http://www.apache.org/licenses/, http://www.apache.org/licenses/LICENSE-2.0, https://spdx.org/licenses/Apache-2.0
Files with this license:
- README.md [235:235]
+ README.md [234:234]
SKIP BSD-3-Clause d77bd60dc7ee5f9c3b221f6edd94bbac
BELONGS ya.make
@@ -231,7 +231,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://www.opensource.org/licenses/BSD-3-Clause, https://spdx.org/licenses/BSD-3-Clause
Files with this license:
- README.md [239:239]
+ README.md [238:238]
SKIP MIT dd09705e3ec59af63c705c8f5f3eadb2
BELONGS ya.make
@@ -243,7 +243,7 @@ BELONGS ya.make
Match type : REFERENCE
Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
Files with this license:
- README.md [233:233]
+ README.md [232:232]
KEEP MIT f0fe4686586f118327c3bc63fe4027de
BELONGS ya.make
diff --git a/contrib/libs/simdjson/.yandex_meta/override.nix b/contrib/libs/simdjson/.yandex_meta/override.nix
index 386e6cc798..a3e6ea867c 100644
--- a/contrib/libs/simdjson/.yandex_meta/override.nix
+++ b/contrib/libs/simdjson/.yandex_meta/override.nix
@@ -1,11 +1,11 @@
pkgs: attrs: with pkgs; rec {
- version = "3.12.1";
+ version = "3.12.2";
src = fetchFromGitHub {
owner = "simdjson";
repo = "simdjson";
rev = "v${version}";
- hash = "sha256-ujeG3yidZJZV6x4RQQYXwbslQcRx3HaqjzgaU2A4cQU=";
+ hash = "sha256-TjUPySFwwTlD4fLpHoUywAeWvVvi7Hg1wxzgE9vohrs=";
};
cmakeFlags = attrs.cmakeFlags ++ [
diff --git a/contrib/libs/simdjson/README.md b/contrib/libs/simdjson/README.md
index d0cae0caa4..a9ca9a08da 100644
--- a/contrib/libs/simdjson/README.md
+++ b/contrib/libs/simdjson/README.md
@@ -1,5 +1,4 @@
-[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
[![][license img]][license] [![][licensemit img]][licensemit]
diff --git a/contrib/libs/simdjson/include/simdjson/icelake/simd.h b/contrib/libs/simdjson/include/simdjson/icelake/simd.h
index 04203f4b9a..a37ef957c9 100644
--- a/contrib/libs/simdjson/include/simdjson/icelake/simd.h
+++ b/contrib/libs/simdjson/include/simdjson/icelake/simd.h
@@ -148,14 +148,18 @@ namespace simd {
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
- // Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
+ // Only the first 64 - count_ones(mask) bytes of the result are significant but 64 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint64_t mask, L * output) const {
- _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
+ // we deliberately avoid _mm512_mask_compressstoreu_epi8 for portability
+ // (AMD Zen4 has terrible performance with it, it is effectively broken)
+ // _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
+ __m512i compressed = _mm512_maskz_compress_epi8(~mask, *this);
+ _mm512_storeu_si512(output, compressed); // could use a mask
}
template<typename L>
diff --git a/contrib/libs/simdjson/include/simdjson/simdjson_version.h b/contrib/libs/simdjson/include/simdjson/simdjson_version.h
index ae27901639..d61c07c01f 100644
--- a/contrib/libs/simdjson/include/simdjson/simdjson_version.h
+++ b/contrib/libs/simdjson/include/simdjson/simdjson_version.h
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
-#define SIMDJSON_VERSION "3.12.1"
+#define SIMDJSON_VERSION "3.12.2"
namespace simdjson {
enum {
@@ -19,7 +19,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
- SIMDJSON_VERSION_REVISION = 1
+ SIMDJSON_VERSION_REVISION = 2
};
} // namespace simdjson
diff --git a/contrib/libs/simdjson/ya.make b/contrib/libs/simdjson/ya.make
index b4c95dae92..1f2e8bb1ea 100644
--- a/contrib/libs/simdjson/ya.make
+++ b/contrib/libs/simdjson/ya.make
@@ -10,9 +10,9 @@ LICENSE(
LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
-VERSION(3.12.1)
+VERSION(3.12.2)
-ORIGINAL_SOURCE(https://github.com/simdjson/simdjson/archive/v3.12.1.tar.gz)
+ORIGINAL_SOURCE(https://github.com/simdjson/simdjson/archive/v3.12.2.tar.gz)
ADDINCL(
GLOBAL contrib/libs/simdjson/include
diff --git a/contrib/python/ipython/py3/.dist-info/METADATA b/contrib/python/ipython/py3/.dist-info/METADATA
index b6a222768c..590a77f53f 100644
--- a/contrib/python/ipython/py3/.dist-info/METADATA
+++ b/contrib/python/ipython/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.2
Name: ipython
-Version: 8.31.0
+Version: 8.32.0
Summary: IPython: Productive Interactive Computing
Author: The IPython Development Team
Author-email: ipython-dev@python.org
@@ -85,6 +85,9 @@ Requires-Dist: matplotlib; extra == "matplotlib"
Provides-Extra: all
Requires-Dist: ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]; extra == "all"
Requires-Dist: ipython[test,test_extra]; extra == "all"
+Dynamic: author
+Dynamic: author-email
+Dynamic: license
IPython provides a rich toolkit to help you make the most out of using Python
interactively. Its main components are:
diff --git a/contrib/python/ipython/py3/IPython/core/interactiveshell.py b/contrib/python/ipython/py3/IPython/core/interactiveshell.py
index 07fb807760..a341ab053a 100644
--- a/contrib/python/ipython/py3/IPython/core/interactiveshell.py
+++ b/contrib/python/ipython/py3/IPython/core/interactiveshell.py
@@ -900,7 +900,7 @@ class InteractiveShell(SingletonConfigurable):
return
p = Path(sys.executable)
- p_venv = Path(os.environ["VIRTUAL_ENV"])
+ p_venv = Path(os.environ["VIRTUAL_ENV"]).resolve()
# fallback venv detection:
# stdlib venv may symlink sys.executable, so we can't use realpath.
@@ -913,7 +913,7 @@ class InteractiveShell(SingletonConfigurable):
drive_name = p_venv.parts[2]
p_venv = (drive_name + ":/") / Path(*p_venv.parts[3:])
- if any(p_venv == p.parents[1] for p in paths):
+ if any(p_venv == p.parents[1].resolve() for p in paths):
# Our exe is inside or has access to the virtualenv, don't need to do anything.
return
@@ -2093,6 +2093,8 @@ class InteractiveShell(SingletonConfigurable):
sys.last_type = etype
sys.last_value = value
sys.last_traceback = tb
+ if sys.version_info >= (3, 12):
+ sys.last_exc = value
return etype, value, tb
diff --git a/contrib/python/ipython/py3/IPython/core/magics/execution.py b/contrib/python/ipython/py3/IPython/core/magics/execution.py
index 3aa0a27fc2..ec17d0a497 100644
--- a/contrib/python/ipython/py3/IPython/core/magics/execution.py
+++ b/contrib/python/ipython/py3/IPython/core/magics/execution.py
@@ -977,7 +977,21 @@ class ExecutionMagics(Magics):
break
finally:
sys.settrace(trace)
-
+
+ # Perform proper cleanup of the session in case if
+ # it exited with "continue" and not "quit" command
+ if hasattr(deb, "rcLines"):
+ # Run this code defensively in case if custom debugger
+ # class does not implement rcLines, which although public
+ # is an implementation detail of `pdb.Pdb` and not part of
+ # the more generic basic debugger framework (`bdb.Bdb`).
+ deb.set_quit()
+ deb.rcLines.extend(["q"])
+ try:
+ deb.run("", code_ns, local_ns)
+ except StopIteration:
+ # Stop iteration is raised on quit command
+ pass
except:
etype, value, tb = sys.exc_info()
diff --git a/contrib/python/ipython/py3/IPython/core/magics/script.py b/contrib/python/ipython/py3/IPython/core/magics/script.py
index 0c405ef420..3bfc4d8d67 100644
--- a/contrib/python/ipython/py3/IPython/core/magics/script.py
+++ b/contrib/python/ipython/py3/IPython/core/magics/script.py
@@ -67,6 +67,10 @@ def script_args(f):
return f
+class RaiseAfterInterrupt(Exception):
+ pass
+
+
@magics_class
class ScriptMagics(Magics):
"""Magics for talking to scripts
@@ -176,6 +180,10 @@ class ScriptMagics(Magics):
The rest of the cell is run by that program.
+ .. versionchanged:: 9.0
+ Interrupting the script executed without `--bg` will end in
+ raising an exception (unless `--no-raise-error` is passed).
+
Examples
--------
::
@@ -212,7 +220,7 @@ class ScriptMagics(Magics):
async def _readchunk(stream):
try:
- return await stream.readuntil(b"\n")
+ return await stream.read(100)
except asyncio.exceptions.IncompleteReadError as e:
return e.partial
except asyncio.exceptions.LimitOverrunError as e:
@@ -292,20 +300,33 @@ class ScriptMagics(Magics):
p.send_signal(signal.SIGINT)
in_thread(asyncio.wait_for(p.wait(), timeout=0.1))
if p.returncode is not None:
- print("Process is interrupted.")
- return
+ print("Process was interrupted.")
+ if args.raise_error:
+ raise RaiseAfterInterrupt()
+ else:
+ return
p.terminate()
in_thread(asyncio.wait_for(p.wait(), timeout=0.1))
if p.returncode is not None:
- print("Process is terminated.")
- return
+ print("Process was terminated.")
+ if args.raise_error:
+ raise RaiseAfterInterrupt()
+ else:
+ return
p.kill()
- print("Process is killed.")
+ print("Process was killed.")
+ if args.raise_error:
+ raise RaiseAfterInterrupt()
+ except RaiseAfterInterrupt:
+ pass
except OSError:
pass
except Exception as e:
print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
- return
+ if args.raise_error:
+ raise CalledProcessError(p.returncode, cell) from None
+ else:
+ return
if args.raise_error and p.returncode != 0:
# If we get here and p.returncode is still None, we must have
diff --git a/contrib/python/ipython/py3/IPython/core/release.py b/contrib/python/ipython/py3/IPython/core/release.py
index 06917bb8ae..a21f446949 100644
--- a/contrib/python/ipython/py3/IPython/core/release.py
+++ b/contrib/python/ipython/py3/IPython/core/release.py
@@ -16,7 +16,7 @@
# release. 'dev' as a _version_extra string means this is a development
# version
_version_major = 8
-_version_minor = 31
+_version_minor = 32
_version_patch = 0
_version_extra = ".dev"
# _version_extra = "rc1"
diff --git a/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py b/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
index ef4f5cd3f6..ba9a31135a 100644
--- a/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
+++ b/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
@@ -26,7 +26,10 @@ from traitlets import (
Any,
validate,
Float,
+ DottedObjectName,
)
+from traitlets.utils.importstring import import_item
+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
@@ -214,7 +217,9 @@ class TerminalInteractiveShell(InteractiveShell):
pt_app: UnionType[PromptSession, None] = None
auto_suggest: UnionType[
- AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None
+ AutoSuggestFromHistory,
+ NavigableAutoSuggestFromHistory,
+ None,
] = None
debugger_history = None
@@ -421,6 +426,37 @@ class TerminalInteractiveShell(InteractiveShell):
allow_none=True,
).tag(config=True)
+ llm_provider_class = DottedObjectName(
+ None,
+ allow_none=True,
+ help="""\
+ Provisional:
+ This is a provisinal API in IPython 8.32, before stabilisation
+ in 9.0, it may change without warnings.
+
+ class to use for the `NavigableAutoSuggestFromHistory` to request
+ completions from a LLM, this should inherit from
+ `jupyter_ai_magics:BaseProvider` and implement
+ `stream_inline_completions`
+ """,
+ ).tag(config=True)
+
+ @observe("llm_provider_class")
+ def _llm_provider_class_changed(self, change):
+ provider_class = change.new
+ if provider_class is not None:
+ warn(
+ "TerminalInteractiveShell.llm_provider_class is a provisional"
+ " API as of IPython 8.32, and may change without warnings."
+ )
+ if isinstance(self.auto_suggest, NavigableAutoSuggestFromHistory):
+ self.auto_suggest._llm_provider = provider_class()
+ else:
+ self.log.warn(
+ "llm_provider_class only has effects when using"
+ "`NavigableAutoSuggestFromHistory` as auto_suggest."
+ )
+
def _set_autosuggestions(self, provider):
# disconnect old handler
if self.auto_suggest and isinstance(
@@ -432,7 +468,15 @@ class TerminalInteractiveShell(InteractiveShell):
elif provider == "AutoSuggestFromHistory":
self.auto_suggest = AutoSuggestFromHistory()
elif provider == "NavigableAutoSuggestFromHistory":
+ # LLM stuff are all Provisional in 8.32
+ if self.llm_provider_class:
+ llm_provider_constructor = import_item(self.llm_provider_class)
+ llm_provider = llm_provider_constructor()
+ else:
+ llm_provider = None
self.auto_suggest = NavigableAutoSuggestFromHistory()
+ # Provisinal in 8.32
+ self.auto_suggest._llm_provider = llm_provider
else:
raise ValueError("No valid provider.")
if self.pt_app:
@@ -815,7 +859,8 @@ class TerminalInteractiveShell(InteractiveShell):
& ~IsDone()
& Condition(
lambda: isinstance(
- self.auto_suggest, NavigableAutoSuggestFromHistory
+ self.auto_suggest,
+ NavigableAutoSuggestFromHistory,
)
),
),
diff --git a/contrib/python/ipython/py3/IPython/terminal/shortcuts/auto_suggest.py b/contrib/python/ipython/py3/IPython/terminal/shortcuts/auto_suggest.py
index 94a94a88c1..bcba5622e4 100644
--- a/contrib/python/ipython/py3/IPython/terminal/shortcuts/auto_suggest.py
+++ b/contrib/python/ipython/py3/IPython/terminal/shortcuts/auto_suggest.py
@@ -1,13 +1,15 @@
import re
+import asyncio
import tokenize
from io import StringIO
-from typing import Callable, List, Optional, Union, Generator, Tuple
+from typing import Callable, List, Optional, Union, Generator, Tuple, ClassVar, Any
import warnings
+import prompt_toolkit
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.key_binding import KeyPressEvent
from prompt_toolkit.key_binding.bindings import named_commands as nc
-from prompt_toolkit.auto_suggest import AutoSuggestFromHistory, Suggestion
+from prompt_toolkit.auto_suggest import AutoSuggestFromHistory, Suggestion, AutoSuggest
from prompt_toolkit.document import Document
from prompt_toolkit.history import History
from prompt_toolkit.shortcuts import PromptSession
@@ -22,6 +24,12 @@ from IPython.utils.tokenutil import generate_tokens
from .filters import pass_through
+try:
+ import jupyter_ai_magics
+ import jupyter_ai.completions.models as jai_models
+except ModuleNotFoundError:
+ jai_models = None
+
def _get_query(document: Document):
return document.lines[document.cursor_position_row]
@@ -31,26 +39,124 @@ class AppendAutoSuggestionInAnyLine(Processor):
"""
Append the auto suggestion to lines other than the last (appending to the
last line is natively supported by the prompt toolkit).
+
+ This has a private `_debug` attribute that can be set to True to display
+ debug information as virtual suggestion on the end of any line. You can do
+ so with:
+
+ >>> from IPython.terminal.shortcuts.auto_suggest import AppendAutoSuggestionInAnyLine
+ >>> AppendAutoSuggestionInAnyLine._debug = True
+
"""
+ _debug: ClassVar[bool] = False
+
def __init__(self, style: str = "class:auto-suggestion") -> None:
self.style = style
def apply_transformation(self, ti: TransformationInput) -> Transformation:
- is_last_line = ti.lineno == ti.document.line_count - 1
- is_active_line = ti.lineno == ti.document.cursor_position_row
+ """
+ Apply transformation to the line that is currently being edited.
- if not is_last_line and is_active_line:
- buffer = ti.buffer_control.buffer
+ This is a variation of the original implementation in prompt toolkit
+ that allows to not only append suggestions to any line, but also to show
+ multi-line suggestions.
- if buffer.suggestion and ti.document.is_cursor_at_the_end_of_line:
- suggestion = buffer.suggestion.text
- else:
- suggestion = ""
+ As transformation are applied on a line-by-line basis; we need to trick
+ a bit, and elide any line that is after the line we are currently
+ editing, until we run out of completions. We cannot shift the existing
+ lines
+
+ There are multiple cases to handle:
+
+ The completions ends before the end of the buffer:
+ We can resume showing the normal line, and say that some code may
+ be hidden.
+
+ The completions ends at the end of the buffer
+ We can just say that some code may be hidden.
+
+ And separately:
+
+ The completions ends beyond the end of the buffer
+ We need to both say that some code may be hidden, and that some
+ lines are not shown.
+
+ """
+ last_line_number = ti.document.line_count - 1
+ is_last_line = ti.lineno == last_line_number
+
+ noop = lambda text: Transformation(
+ fragments=ti.fragments + [(self.style, " " + text if self._debug else "")]
+ )
+ if ti.document.line_count == 1:
+ return noop("noop:oneline")
+ if ti.document.cursor_position_row == last_line_number and is_last_line:
+ # prompt toolkit already appends something; just leave it be
+ return noop("noop:last line and cursor")
+
+ # first everything before the current line is unchanged.
+ if ti.lineno < ti.document.cursor_position_row:
+ return noop("noop:before cursor")
+
+ buffer = ti.buffer_control.buffer
+ if not buffer.suggestion or not ti.document.is_cursor_at_the_end_of_line:
+ return noop("noop:not eol")
+
+ delta = ti.lineno - ti.document.cursor_position_row
+ suggestions = buffer.suggestion.text.splitlines()
+ if len(suggestions) == 0:
+ return noop("noop: no suggestions")
+
+ suggestions_longer_than_buffer: bool = (
+ len(suggestions) + ti.document.cursor_position_row > ti.document.line_count
+ )
+
+ if len(suggestions) >= 1 and prompt_toolkit.VERSION < (3, 0, 49):
+ if ti.lineno == ti.document.cursor_position_row:
+ return Transformation(
+ fragments=ti.fragments
+ + [
+ (
+ "red",
+ "(Cannot show multiline suggestion; requires prompt_toolkit > 3.0.49)",
+ )
+ ]
+ )
+ else:
+ return Transformation(fragments=ti.fragments)
+ if delta == 0:
+ suggestion = suggestions[0]
return Transformation(fragments=ti.fragments + [(self.style, suggestion)])
+ if is_last_line:
+ if delta < len(suggestions):
+ extra = f"; {len(suggestions) - delta} line(s) hidden"
+ suggestion = f"… rest of suggestion ({len(suggestions) - delta} lines) and code hidden"
+ return Transformation([(self.style, suggestion)])
+
+ n_elided = len(suggestions)
+ for i in range(len(suggestions)):
+ ll = ti.get_line(last_line_number - i)
+ el = "".join(l[1] for l in ll).strip()
+ if el:
+ break
+ else:
+ n_elided -= 1
+ if n_elided:
+ return Transformation([(self.style, f"… {n_elided} line(s) hidden")])
+ else:
+ return Transformation(
+ ti.get_line(last_line_number - len(suggestions) + 1)
+ + ([(self.style, "shift-last-line")] if self._debug else [])
+ )
+
+ elif delta < len(suggestions):
+ suggestion = suggestions[delta]
+ return Transformation([(self.style, suggestion)])
else:
- return Transformation(fragments=ti.fragments)
+ shift = ti.lineno - len(suggestions) + 1
+ return Transformation(ti.get_line(shift))
class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
@@ -60,16 +166,29 @@ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
state need to carefully be cleared on the right events.
"""
- def __init__(
- self,
- ):
+ skip_lines: int
+ _connected_apps: list[PromptSession]
+
+ # handle to the currently running llm task that appends suggestions to the
+ # current buffer; we keep a handle to it in order to cancell it when there is a cursor movement, or
+ # another request.
+ _llm_task: asyncio.Task | None = None
+
+ # This is the instance of the LLM provider from jupyter-ai to which we forward the request
+ # to generate inline completions.
+ _llm_provider: Any | None
+
+ def __init__(self):
+ super().__init__()
self.skip_lines = 0
self._connected_apps = []
+ self._llm_provider = None
def reset_history_position(self, _: Buffer):
self.skip_lines = 0
- def disconnect(self):
+ def disconnect(self) -> None:
+ self._cancel_running_llm_task()
for pt_app in self._connected_apps:
text_insert_event = pt_app.default_buffer.on_text_insert
text_insert_event.remove_handler(self.reset_history_position)
@@ -94,7 +213,8 @@ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
return None
- def _dismiss(self, buffer, *args, **kwargs):
+ def _dismiss(self, buffer, *args, **kwargs) -> None:
+ self._cancel_running_llm_task()
buffer.suggestion = None
def _find_match(
@@ -149,6 +269,7 @@ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
)
def up(self, query: str, other_than: str, history: History) -> None:
+ self._cancel_running_llm_task()
for suggestion, line_number in self._find_next_match(
query, self.skip_lines, history
):
@@ -165,6 +286,7 @@ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
self.skip_lines = 0
def down(self, query: str, other_than: str, history: History) -> None:
+ self._cancel_running_llm_task()
for suggestion, line_number in self._find_previous_match(
query, self.skip_lines, history
):
@@ -180,6 +302,131 @@ class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):
self.skip_lines = line_number
break
+ def _cancel_running_llm_task(self) -> None:
+ """
+ Try to cancell the currently running llm_task if exists, and set it to None.
+ """
+ if self._llm_task is not None:
+ if self._llm_task.done():
+ self._llm_task = None
+ return
+ cancelled = self._llm_task.cancel()
+ if cancelled:
+ self._llm_task = None
+ if not cancelled:
+ warnings.warn(
+ "LLM task not cancelled, does your provider support cancellation?"
+ )
+
+ async def _trigger_llm(self, buffer) -> None:
+ """
+ This will ask the current llm provider a suggestion for the current buffer.
+
+ If there is a currently running llm task, it will cancel it.
+ """
+ # we likely want to store the current cursor position, and cancel if the cursor has moved.
+ if not self._llm_provider:
+ warnings.warn("No LLM provider found, cannot trigger LLM completions")
+ return
+ if jai_models is None:
+ warnings.warn(
+ "LLM Completion requires `jupyter_ai_magics` and `jupyter_ai` to be installed"
+ )
+
+ self._cancel_running_llm_task()
+
+ async def error_catcher(buffer):
+ """
+ This catches and log any errors, as otherwise this is just
+ lost in the void of the future running task.
+ """
+ try:
+ await self._trigger_llm_core(buffer)
+ except Exception as e:
+ get_ipython().log.error("error")
+ raise
+
+ # here we need a cancellable task so we can't just await the error catched
+ self._llm_task = asyncio.create_task(error_catcher(buffer))
+ await self._llm_task
+
+ async def _trigger_llm_core(self, buffer: Buffer):
+ """
+ This is the core of the current llm request.
+
+ Here we build a compatible `InlineCompletionRequest` and ask the llm
+ provider to stream it's response back to us iteratively setting it as
+ the suggestion on the current buffer.
+
+ Unlike with JupyterAi, as we do not have multiple cell, the cell number
+ is always set to `0`, note that we _could_ set it to a new number each
+ time and ignore threply from past numbers.
+
+ We set the prefix to the current cell content, but could also inset the
+ rest of the history or even just the non-fail history.
+
+ In the same way, we do not have cell id.
+
+ LLM provider may return multiple suggestion stream, but for the time
+ being we only support one.
+
+ Here we make the assumption that the provider will have
+ stream_inline_completions, I'm not sure it is the case for all
+ providers.
+ """
+
+ request = jai_models.InlineCompletionRequest(
+ number=0,
+ prefix=buffer.document.text,
+ suffix="",
+ mime="text/x-python",
+ stream=True,
+ path=None,
+ language="python",
+ cell_id=None,
+ )
+
+ async for reply_and_chunks in self._llm_provider.stream_inline_completions(
+ request
+ ):
+ if isinstance(reply_and_chunks, jai_models.InlineCompletionReply):
+ if len(reply_and_chunks.list.items) > 1:
+ raise ValueError(
+ "Terminal IPython cannot deal with multiple LLM suggestions at once"
+ )
+ buffer.suggestion = Suggestion(
+ reply_and_chunks.list.items[0].insertText
+ )
+ buffer.on_suggestion_set.fire()
+ elif isinstance(reply_and_chunks, jai_models.InlineCompletionStreamChunk):
+ buffer.suggestion = Suggestion(reply_and_chunks.response.insertText)
+ buffer.on_suggestion_set.fire()
+ return
+
+
+_MIN_LINES = 5
+
+
+async def llm_autosuggestion(event: KeyPressEvent):
+ """
+ Ask the AutoSuggester from history to delegate to ask an LLM for completion
+
+ This will first make sure that the current buffer have _MIN_LINES (7)
+ available lines to insert the LLM completion
+
+ Provisional as of 8.32, may change without warnigns
+
+ """
+ provider = get_ipython().auto_suggest
+ if not isinstance(provider, NavigableAutoSuggestFromHistory):
+ return
+ doc = event.current_buffer.document
+ lines_to_insert = max(0, _MIN_LINES - doc.line_count + doc.cursor_position_row)
+ for _ in range(lines_to_insert):
+ event.current_buffer.insert_text("\n", move_cursor=False)
+
+ await provider._trigger_llm(event.current_buffer)
+
def accept_or_jump_to_end(event: KeyPressEvent):
"""Apply autosuggestion or jump to end of line."""
diff --git a/contrib/python/ipython/py3/IPython/utils/_sysinfo.py b/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
index 44fbbc4530..fbb89d3aa8 100644
--- a/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
+++ b/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
@@ -1,2 +1,2 @@
# GENERATED BY setup.py
-commit = "22d6a1c16"
+commit = "56a70e42d"
diff --git a/contrib/python/ipython/py3/ya.make b/contrib/python/ipython/py3/ya.make
index 950e693736..adc99f5b64 100644
--- a/contrib/python/ipython/py3/ya.make
+++ b/contrib/python/ipython/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(8.31.0)
+VERSION(8.32.0)
LICENSE(BSD-3-Clause)