##// END OF EJS Templates
Merge branch 'main' into completion-matcher
Michał Krassowski -
r27777:b0daec19 merge
parent child Browse files
Show More
@@ -7,11 +7,11 b' jobs:'
7 7 runs-on: ubuntu-latest
8 8
9 9 steps:
10 - uses: actions/checkout@v2
11 - name: Set up Python 3.8
12 uses: actions/setup-python@v2
10 - uses: actions/checkout@v3
11 - name: Set up Python
12 uses: actions/setup-python@v4
13 13 with:
14 python-version: 3.8
14 python-version: 3.x
15 15 - name: Install Graphviz
16 16 run: |
17 17 sudo apt-get update
@@ -21,9 +21,9 b' jobs:'
21 21 python-version: "3.9"
22 22
23 23 steps:
24 - uses: actions/checkout@v2
24 - uses: actions/checkout@v3
25 25 - name: Set up Python ${{ matrix.python-version }}
26 uses: actions/setup-python@v2
26 uses: actions/setup-python@v4
27 27 with:
28 28 python-version: ${{ matrix.python-version }}
29 29 - name: Update Python installer
@@ -15,9 +15,9 b' jobs:'
15 15 python-version: [3.8]
16 16
17 17 steps:
18 - uses: actions/checkout@v2
18 - uses: actions/checkout@v3
19 19 - name: Set up Python ${{ matrix.python-version }}
20 uses: actions/setup-python@v2
20 uses: actions/setup-python@v4
21 21 with:
22 22 python-version: ${{ matrix.python-version }}
23 23 - name: Install dependencies
@@ -14,18 +14,14 b' jobs:'
14 14
15 15 runs-on: ubuntu-latest
16 16 timeout-minutes: 5
17 strategy:
18 matrix:
19 python-version: [3.8]
20
21 17 steps:
22 - uses: actions/checkout@v2
18 - uses: actions/checkout@v3
23 19 with:
24 20 fetch-depth: 0
25 - name: Set up Python ${{ matrix.python-version }}
26 uses: actions/setup-python@v2
21 - name: Set up Python
22 uses: actions/setup-python@v4
27 23 with:
28 python-version: ${{ matrix.python-version }}
24 python-version: 3.x
29 25 - name: Install dependencies
30 26 run: |
31 27 python -m pip install --upgrade pip
@@ -49,9 +49,9 b' jobs:'
49 49 deps: test
50 50
51 51 steps:
52 - uses: actions/checkout@v2
52 - uses: actions/checkout@v3
53 53 - name: Set up Python ${{ matrix.python-version }}
54 uses: actions/setup-python@v2
54 uses: actions/setup-python@v4
55 55 with:
56 56 python-version: ${{ matrix.python-version }}
57 57 cache: pip
@@ -906,19 +906,23 b' class Completer(Configurable):'
906 906 matches = []
907 907 match_append = matches.append
908 908 n = len(text)
909 for lst in [keyword.kwlist,
910 builtin_mod.__dict__.keys(),
911 self.namespace.keys(),
912 self.global_namespace.keys()]:
909 for lst in [
910 keyword.kwlist,
911 builtin_mod.__dict__.keys(),
912 list(self.namespace.keys()),
913 list(self.global_namespace.keys()),
914 ]:
913 915 for word in lst:
914 916 if word[:n] == text and word != "__builtins__":
915 917 match_append(word)
916 918
917 919 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
918 for lst in [self.namespace.keys(),
919 self.global_namespace.keys()]:
920 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
921 for word in lst if snake_case_re.match(word)}
920 for lst in [list(self.namespace.keys()), list(self.global_namespace.keys())]:
921 shortened = {
922 "_".join([sub[0] for sub in word.split("_")]): word
923 for word in lst
924 if snake_case_re.match(word)
925 }
922 926 for word in shortened.keys():
923 927 if word[:n] == text and word != "__builtins__":
924 928 match_append(shortened[word])
@@ -202,7 +202,6 b' class HistoryAccessor(HistoryAccessorBase):'
202 202 config : :class:`~traitlets.config.loader.Config`
203 203 Config object. hist_file can also be set through this.
204 204 """
205 # We need a pointer back to the shell for various tasks.
206 205 super(HistoryAccessor, self).__init__(**traits)
207 206 # defer setting hist_file from kwarg until after init,
208 207 # otherwise the default kwarg value would clobber any value
@@ -344,11 +343,6 b' class HistoryAccessor(HistoryAccessorBase):'
344 343 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
345 344 """Get the last n lines from the history database.
346 345
347 Most recent entry last.
348
349 Completion will be reordered so that that the last ones are when
350 possible from current session.
351
352 346 Parameters
353 347 ----------
354 348 n : int
@@ -367,31 +361,12 b' class HistoryAccessor(HistoryAccessorBase):'
367 361 self.writeout_cache()
368 362 if not include_latest:
369 363 n += 1
370 # cursor/line/entry
371 this_cur = list(
372 self._run_sql(
373 "WHERE session == ? ORDER BY line DESC LIMIT ? ",
374 (self.session_number, n),
375 raw=raw,
376 output=output,
377 )
378 )
379 other_cur = list(
380 self._run_sql(
381 "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?",
382 (self.session_number, n),
383 raw=raw,
384 output=output,
385 )
364 cur = self._run_sql(
365 "ORDER BY session DESC, line DESC LIMIT ?", (n,), raw=raw, output=output
386 366 )
387
388 everything = this_cur + other_cur
389
390 everything = everything[:n]
391
392 367 if not include_latest:
393 return list(everything)[:0:-1]
394 return list(everything)[::-1]
368 return reversed(list(cur)[1:])
369 return reversed(list(cur))
395 370
396 371 @catch_corrupt_db
397 372 def search(self, pattern="*", raw=True, search_raw=True,
@@ -560,7 +535,6 b' class HistoryManager(HistoryAccessor):'
560 535 def __init__(self, shell=None, config=None, **traits):
561 536 """Create a new history manager associated with a shell instance.
562 537 """
563 # We need a pointer back to the shell for various tasks.
564 538 super(HistoryManager, self).__init__(shell=shell, config=config,
565 539 **traits)
566 540 self.save_flag = threading.Event()
@@ -656,6 +630,59 b' class HistoryManager(HistoryAccessor):'
656 630
657 631 return super(HistoryManager, self).get_session_info(session=session)
658 632
633 @catch_corrupt_db
634 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
635 """Get the last n lines from the history database.
636
637 Most recent entry last.
638
639 Completion will be reordered so that that the last ones are when
640 possible from current session.
641
642 Parameters
643 ----------
644 n : int
645 The number of lines to get
646 raw, output : bool
647 See :meth:`get_range`
648 include_latest : bool
649 If False (default), n+1 lines are fetched, and the latest one
650 is discarded. This is intended to be used where the function
651 is called by a user command, which it should not return.
652
653 Returns
654 -------
655 Tuples as :meth:`get_range`
656 """
657 self.writeout_cache()
658 if not include_latest:
659 n += 1
660 # cursor/line/entry
661 this_cur = list(
662 self._run_sql(
663 "WHERE session == ? ORDER BY line DESC LIMIT ? ",
664 (self.session_number, n),
665 raw=raw,
666 output=output,
667 )
668 )
669 other_cur = list(
670 self._run_sql(
671 "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?",
672 (self.session_number, n),
673 raw=raw,
674 output=output,
675 )
676 )
677
678 everything = this_cur + other_cur
679
680 everything = everything[:n]
681
682 if not include_latest:
683 return list(everything)[:0:-1]
684 return list(everything)[::-1]
685
659 686 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
660 687 """Get input and output history from the current session. Called by
661 688 get_range, and takes similar parameters."""
@@ -155,15 +155,17 b' def clipboard_get(self):'
155 155 """ Get text from the clipboard.
156 156 """
157 157 from ..lib.clipboard import (
158 osx_clipboard_get, tkinter_clipboard_get,
159 win32_clipboard_get
158 osx_clipboard_get,
159 tkinter_clipboard_get,
160 win32_clipboard_get,
161 wayland_clipboard_get,
160 162 )
161 163 if sys.platform == 'win32':
162 164 chain = [win32_clipboard_get, tkinter_clipboard_get]
163 165 elif sys.platform == 'darwin':
164 166 chain = [osx_clipboard_get, tkinter_clipboard_get]
165 167 else:
166 chain = [tkinter_clipboard_get]
168 chain = [wayland_clipboard_get, tkinter_clipboard_get]
167 169 dispatcher = CommandChainDispatcher()
168 170 for func in chain:
169 171 dispatcher.add(func)
@@ -8,6 +8,7 b' builtin.'
8 8
9 9 import io
10 10 import os
11 import pathlib
11 12 import re
12 13 import sys
13 14 from pprint import pformat
@@ -409,7 +410,7 b' class OSMagics(Magics):'
409 410 except OSError:
410 411 print(sys.exc_info()[1])
411 412 else:
412 cwd = os.getcwd()
413 cwd = pathlib.Path.cwd()
413 414 dhist = self.shell.user_ns['_dh']
414 415 if oldcwd != cwd:
415 416 dhist.append(cwd)
@@ -419,7 +420,7 b' class OSMagics(Magics):'
419 420 os.chdir(self.shell.home_dir)
420 421 if hasattr(self.shell, 'term_title') and self.shell.term_title:
421 422 set_term_title(self.shell.term_title_format.format(cwd="~"))
422 cwd = os.getcwd()
423 cwd = pathlib.Path.cwd()
423 424 dhist = self.shell.user_ns['_dh']
424 425
425 426 if oldcwd != cwd:
@@ -16,7 +16,7 b''
16 16 # release. 'dev' as a _version_extra string means this is a development
17 17 # version
18 18 _version_major = 8
19 _version_minor = 5
19 _version_minor = 6
20 20 _version_patch = 0
21 21 _version_extra = ".dev"
22 22 # _version_extra = "rc1"
@@ -1,2 +1,3 b''
1 1 import sys
2
2 3 print(sys.argv[1:])
@@ -8,6 +8,7 b" with better-isolated tests that don't rely on the global instance in iptest."
8 8 from IPython.core.splitinput import LineInfo
9 9 from IPython.core.prefilter import AutocallChecker
10 10
11
11 12 def doctest_autocall():
12 13 """
13 14 In [1]: def f1(a,b,c):
@@ -28,7 +28,7 b' def test_output_quiet():'
28 28 with AssertNotPrints('2'):
29 29 ip.run_cell('1+1;\n#commented_out_function()', store_history=True)
30 30
31 def test_underscore_no_overrite_user():
31 def test_underscore_no_overwrite_user():
32 32 ip.run_cell('_ = 42', store_history=True)
33 33 ip.run_cell('1+1', store_history=True)
34 34
@@ -41,7 +41,7 b' def test_underscore_no_overrite_user():'
41 41 ip.run_cell('_', store_history=True)
42 42
43 43
44 def test_underscore_no_overrite_builtins():
44 def test_underscore_no_overwrite_builtins():
45 45 ip.run_cell("import gettext ; gettext.install('foo')", store_history=True)
46 46 ip.run_cell('3+3', store_history=True)
47 47
@@ -17,7 +17,7 b' from tempfile import TemporaryDirectory'
17 17 # our own packages
18 18 from traitlets.config.loader import Config
19 19
20 from IPython.core.history import HistoryManager, extract_hist_ranges
20 from IPython.core.history import HistoryAccessor, HistoryManager, extract_hist_ranges
21 21
22 22
23 23 def test_proper_default_encoding():
@@ -227,3 +227,81 b' def test_histmanager_disabled():'
227 227
228 228 # hist_file should not be created
229 229 assert hist_file.exists() is False
230
231
232 def test_get_tail_session_awareness():
233 """Test .get_tail() is:
234 - session specific in HistoryManager
235 - session agnostic in HistoryAccessor
236 same for .get_last_session_id()
237 """
238 ip = get_ipython()
239 with TemporaryDirectory() as tmpdir:
240 tmp_path = Path(tmpdir)
241 hist_file = tmp_path / "history.sqlite"
242 get_source = lambda x: x[2]
243 hm1 = None
244 hm2 = None
245 ha = None
246 try:
247 # hm1 creates a new session and adds history entries,
248 # ha catches up
249 hm1 = HistoryManager(shell=ip, hist_file=hist_file)
250 hm1_last_sid = hm1.get_last_session_id
251 ha = HistoryAccessor(hist_file=hist_file)
252 ha_last_sid = ha.get_last_session_id
253
254 hist1 = ["a=1", "b=1", "c=1"]
255 for i, h in enumerate(hist1 + [""], start=1):
256 hm1.store_inputs(i, h)
257 assert list(map(get_source, hm1.get_tail())) == hist1
258 assert list(map(get_source, ha.get_tail())) == hist1
259 sid1 = hm1_last_sid()
260 assert sid1 is not None
261 assert ha_last_sid() == sid1
262
263 # hm2 creates a new session and adds entries,
264 # ha catches up
265 hm2 = HistoryManager(shell=ip, hist_file=hist_file)
266 hm2_last_sid = hm2.get_last_session_id
267
268 hist2 = ["a=2", "b=2", "c=2"]
269 for i, h in enumerate(hist2 + [""], start=1):
270 hm2.store_inputs(i, h)
271 tail = hm2.get_tail(n=3)
272 assert list(map(get_source, tail)) == hist2
273 tail = ha.get_tail(n=3)
274 assert list(map(get_source, tail)) == hist2
275 sid2 = hm2_last_sid()
276 assert sid2 is not None
277 assert ha_last_sid() == sid2
278 assert sid2 != sid1
279
280 # but hm1 still maintains its point of reference
281 # and adding more entries to it doesn't change others
282 # immediate perspective
283 assert hm1_last_sid() == sid1
284 tail = hm1.get_tail(n=3)
285 assert list(map(get_source, tail)) == hist1
286
287 hist3 = ["a=3", "b=3", "c=3"]
288 for i, h in enumerate(hist3 + [""], start=5):
289 hm1.store_inputs(i, h)
290 tail = hm1.get_tail(n=7)
291 assert list(map(get_source, tail)) == hist1 + [""] + hist3
292 tail = hm2.get_tail(n=3)
293 assert list(map(get_source, tail)) == hist2
294 tail = ha.get_tail(n=3)
295 assert list(map(get_source, tail)) == hist2
296 assert hm1_last_sid() == sid1
297 assert hm2_last_sid() == sid2
298 assert ha_last_sid() == sid2
299 finally:
300 if hm1:
301 hm1.save_thread.stop()
302 hm1.db.close()
303 if hm2:
304 hm2.save_thread.stop()
305 hm2.db.close()
306 if ha:
307 ha.db.close()
@@ -32,6 +32,7 b' tests.append(("P\xc3\xa9rez Fernando", ("", "", "P\xc3\xa9rez", "Fernando")))'
32 32 def test_split_user_input():
33 33 return tt.check_pairs(split_user_input, tests)
34 34
35
35 36 def test_LineInfo():
36 37 """Simple test for LineInfo construction and str()"""
37 38 linfo = LineInfo(" %cd /home")
@@ -300,7 +300,7 b' def update_instances(old, new):'
300 300
301 301 for ref in refs:
302 302 if type(ref) is old:
303 ref.__class__ = new
303 object.__setattr__(ref, "__class__", new)
304 304
305 305
306 306 def update_class(old, new):
@@ -1,14 +1,16 b''
1 1 """ Utilities for accessing the platform's clipboard.
2 2 """
3
3 import os
4 4 import subprocess
5 5
6 6 from IPython.core.error import TryNext
7 7 import IPython.utils.py3compat as py3compat
8 8
9
9 10 class ClipboardEmpty(ValueError):
10 11 pass
11 12
13
12 14 def win32_clipboard_get():
13 15 """ Get the current clipboard's text on Windows.
14 16
@@ -32,6 +34,7 b' def win32_clipboard_get():'
32 34 win32clipboard.CloseClipboard()
33 35 return text
34 36
37
35 38 def osx_clipboard_get() -> str:
36 39 """ Get the clipboard's text on OS X.
37 40 """
@@ -43,6 +46,7 b' def osx_clipboard_get() -> str:'
43 46 text = py3compat.decode(bytes_)
44 47 return text
45 48
49
46 50 def tkinter_clipboard_get():
47 51 """ Get the clipboard's text using Tkinter.
48 52
@@ -67,3 +71,31 b' def tkinter_clipboard_get():'
67 71 return text
68 72
69 73
74 def wayland_clipboard_get():
75 """Get the clipboard's text under Wayland using wl-paste command.
76
77 This requires Wayland and wl-clipboard installed and running.
78 """
79 if os.environ.get("XDG_SESSION_TYPE") != "wayland":
80 raise TryNext("wayland is not detected")
81
82 try:
83 with subprocess.Popen(["wl-paste"], stdout=subprocess.PIPE) as p:
84 raw, err = p.communicate()
85 if p.wait():
86 raise TryNext(err)
87 except FileNotFoundError as e:
88 raise TryNext(
89 "Getting text from the clipboard under Wayland requires the wl-clipboard "
90 "extension: https://github.com/bugaevc/wl-clipboard"
91 ) from e
92
93 if not raw:
94 raise ClipboardEmpty
95
96 try:
97 text = py3compat.decode(raw)
98 except UnicodeDecodeError as e:
99 raise ClipboardEmpty from e
100
101 return text
@@ -144,19 +144,30 b" def latex_to_png_dvipng(s, wrap, color='Black', scale=1.0):"
144 144 find_cmd('dvipng')
145 145 except FindCmdError:
146 146 return None
147
148 startupinfo = None
149 if os.name == "nt":
150 # prevent popup-windows
151 startupinfo = subprocess.STARTUPINFO()
152 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
153
147 154 try:
148 155 workdir = Path(tempfile.mkdtemp())
149 tmpfile = workdir.joinpath("tmp.tex")
150 dvifile = workdir.joinpath("tmp.dvi")
151 outfile = workdir.joinpath("tmp.png")
156 tmpfile = "tmp.tex"
157 dvifile = "tmp.dvi"
158 outfile = "tmp.png"
152 159
153 with tmpfile.open("w", encoding="utf8") as f:
160 with workdir.joinpath(tmpfile).open("w", encoding="utf8") as f:
154 161 f.writelines(genelatex(s, wrap))
155 162
156 163 with open(os.devnull, 'wb') as devnull:
157 164 subprocess.check_call(
158 165 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
159 cwd=workdir, stdout=devnull, stderr=devnull)
166 cwd=workdir,
167 stdout=devnull,
168 stderr=devnull,
169 startupinfo=startupinfo,
170 )
160 171
161 172 resolution = round(150*scale)
162 173 subprocess.check_call(
@@ -179,9 +190,10 b" def latex_to_png_dvipng(s, wrap, color='Black', scale=1.0):"
179 190 cwd=workdir,
180 191 stdout=devnull,
181 192 stderr=devnull,
193 startupinfo=startupinfo,
182 194 )
183 195
184 with outfile.open("rb") as f:
196 with workdir.joinpath(outfile).open("rb") as f:
185 197 return f.read()
186 198 except subprocess.CalledProcessError:
187 199 return None
@@ -908,6 +908,8 b' def _deque_pprint(obj, p, cycle):'
908 908 cls_ctor = CallExpression.factory(obj.__class__.__name__)
909 909 if cycle:
910 910 p.pretty(cls_ctor(RawText("...")))
911 elif obj.maxlen is not None:
912 p.pretty(cls_ctor(list(obj), maxlen=obj.maxlen))
911 913 else:
912 914 p.pretty(cls_ctor(list(obj)))
913 915
@@ -2,6 +2,7 b' from IPython.core.error import TryNext'
2 2 from IPython.lib.clipboard import ClipboardEmpty
3 3 from IPython.testing.decorators import skip_if_no_x11
4 4
5
5 6 @skip_if_no_x11
6 7 def test_clipboard_get():
7 8 # Smoketest for clipboard access - we can't easily guarantee that the
@@ -91,7 +91,12 b' def get_default_editor():'
91 91 # - no isatty method
92 92 for _name in ('stdin', 'stdout', 'stderr'):
93 93 _stream = getattr(sys, _name)
94 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
94 try:
95 if not _stream or not hasattr(_stream, "isatty") or not _stream.isatty():
96 _is_tty = False
97 break
98 except ValueError:
99 # stream is closed
95 100 _is_tty = False
96 101 break
97 102 else:
@@ -48,10 +48,17 b' def _elide_point(string:str, *, min_elide=30)->str:'
48 48 file_parts.pop()
49 49
50 50 if len(object_parts) > 3:
51 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1])
51 return "{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}".format(
52 object_parts[0],
53 object_parts[1][:1],
54 object_parts[-2][-1:],
55 object_parts[-1],
56 )
52 57
53 58 elif len(file_parts) > 3:
54 return ('{}' + os.sep + '{}\N{HORIZONTAL ELLIPSIS}{}' + os.sep + '{}').format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1])
59 return ("{}" + os.sep + "{}\N{HORIZONTAL ELLIPSIS}{}" + os.sep + "{}").format(
60 file_parts[0], file_parts[1][:1], file_parts[-2][-1:], file_parts[-1]
61 )
55 62
56 63 return string
57 64
@@ -140,6 +140,18 b' def create_ipython_shortcuts(shell):'
140 140 _following_text_cache[pattern] = condition
141 141 return condition
142 142
143 @Condition
144 def not_inside_unclosed_string():
145 app = get_app()
146 s = app.current_buffer.document.text_before_cursor
147 # remove escaped quotes
148 s = s.replace('\\"', "").replace("\\'", "")
149 # remove triple-quoted string literals
150 s = re.sub(r"(?:\"\"\"[\s\S]*\"\"\"|'''[\s\S]*''')", "", s)
151 # remove single-quoted string literals
152 s = re.sub(r"""(?:"[^"]*["\n]|'[^']*['\n])""", "", s)
153 return not ('"' in s or "'" in s)
154
143 155 # auto match
144 156 @kb.add("(", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
145 157 def _(event):
@@ -160,7 +172,7 b' def create_ipython_shortcuts(shell):'
160 172 '"',
161 173 filter=focused_insert
162 174 & auto_match
163 & preceding_text(r'^([^"]+|"[^"]*")*$')
175 & not_inside_unclosed_string
164 176 & following_text(r"[,)}\]]|$"),
165 177 )
166 178 def _(event):
@@ -171,13 +183,35 b' def create_ipython_shortcuts(shell):'
171 183 "'",
172 184 filter=focused_insert
173 185 & auto_match
174 & preceding_text(r"^([^']+|'[^']*')*$")
186 & not_inside_unclosed_string
175 187 & following_text(r"[,)}\]]|$"),
176 188 )
177 189 def _(event):
178 190 event.current_buffer.insert_text("''")
179 191 event.current_buffer.cursor_left()
180 192
193 @kb.add(
194 '"',
195 filter=focused_insert
196 & auto_match
197 & not_inside_unclosed_string
198 & preceding_text(r'^.*""$'),
199 )
200 def _(event):
201 event.current_buffer.insert_text('""""')
202 event.current_buffer.cursor_left(3)
203
204 @kb.add(
205 "'",
206 filter=focused_insert
207 & auto_match
208 & not_inside_unclosed_string
209 & preceding_text(r"^.*''$"),
210 )
211 def _(event):
212 event.current_buffer.insert_text("''''")
213 event.current_buffer.cursor_left(3)
214
181 215 # raw string
182 216 @kb.add(
183 217 "(", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
@@ -7,6 +7,7 b' import warnings'
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10
10 11 class preserve_keys(object):
11 12 """Preserve a set of keys in a dictionary.
12 13
@@ -1,4 +1,3 b''
1
2 1 from warnings import warn
3 2
4 3 warn("IPython.utils.eventful has moved to traitlets.eventful", stacklevel=2)
@@ -1,4 +1,3 b''
1
2 1 from warnings import warn
3 2
4 3 warn("IPython.utils.log has moved to traitlets.log", stacklevel=2)
@@ -83,12 +83,13 b' def get_py_filename(name):'
83 83 """
84 84
85 85 name = os.path.expanduser(name)
86 if not os.path.isfile(name) and not name.endswith('.py'):
87 name += '.py'
88 86 if os.path.isfile(name):
89 87 return name
90 else:
91 raise IOError('File `%r` not found.' % name)
88 if not name.endswith(".py"):
89 py_name = name + ".py"
90 if os.path.isfile(py_name):
91 return py_name
92 raise IOError("File `%r` not found." % name)
92 93
93 94
94 95 def filefind(filename: str, path_dirs=None) -> str:
@@ -48,6 +48,7 b' class TemporaryWorkingDirectory(TemporaryDirectory):'
48 48 with TemporaryWorkingDirectory() as tmpdir:
49 49 ...
50 50 """
51
51 52 def __enter__(self):
52 53 self.old_wd = Path.cwd()
53 54 _os.chdir(self.name)
@@ -25,6 +25,7 b' Need to be updated:'
25 25
26 26
27 27
28
28 29 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
29 30
30 31 Backwards incompatible changes
@@ -2,6 +2,122 b''
2 2 8.x Series
3 3 ============
4 4
5 .. _version 8.5.0:
6
7 IPython 8.5.0
8 -------------
9
10 First release since a couple of month due to various reasons and timing preventing
11 me for sticking to the usual monthly release the last Friday of each month. This
12 is of non negligible size as it has more than two dozen PRs with various fixes
13 an bug fixes.
14
15 Many thanks to everybody who contributed PRs for your patience in review and
16 merges.
17
18 Here is a non exhaustive list of changes that have been implemented for IPython
19 8.5.0. As usual you can find the full list of issues and PRs tagged with `the
20 8.5 milestone
21 <https://github.com/ipython/ipython/pulls?q=is%3Aclosed+milestone%3A8.5+>`__.
22
23 - Added shortcut for accepting auto suggestion. The End key shortcut for
24 accepting auto-suggestion This binding works in Vi mode too, provided
25 ``TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode`` is set to be
26 ``True`` :ghpull:`13566`.
27
28 - No popup in window for latex generation w hen generating latex (e.g. via
29 `_latex_repr_`) no popup window is shows under Windows. :ghpull:`13679`
30
31 - Fixed error raised when attempting to tab-complete an input string with
32 consecutive periods or forward slashes (such as "file:///var/log/...").
33 :ghpull:`13675`
34
35 - Relative filenames in Latex rendering :
36 The `latex_to_png_dvipng` command internally generates input and output file
37 arguments to `latex` and `dvipis`. These arguments are now generated as
38 relative files to the current working directory instead of absolute file
39 paths. This solves a problem where the current working directory contains
40 characters that are not handled properly by `latex` and `dvips`. There are
41 no changes to the user API. :ghpull:`13680`
42
43 - Stripping decorators bug: Fixed bug which meant that ipython code blocks in
44 restructured text documents executed with the ipython-sphinx extension
45 skipped any lines of code containing python decorators. :ghpull:`13612`
46
47 - Allow some modules with frozen dataclasses to be reloaded. :ghpull:`13732`
48 - Fix paste magic on wayland. :ghpull:`13671`
49 - show maxlen in deque's repr. :ghpull:`13648`
50
51 Restore line numbers for Input
52 ------------------------------
53
54 Line number information in tracebacks from input are restored.
55 Line numbers from input were removed during the transition to v8 enhanced traceback reporting.
56
57 So, instead of::
58
59 ---------------------------------------------------------------------------
60 ZeroDivisionError Traceback (most recent call last)
61 Input In [3], in <cell line: 1>()
62 ----> 1 myfunc(2)
63
64 Input In [2], in myfunc(z)
65 1 def myfunc(z):
66 ----> 2 foo.boo(z-1)
67
68 File ~/code/python/ipython/foo.py:3, in boo(x)
69 2 def boo(x):
70 ----> 3 return 1/(1-x)
71
72 ZeroDivisionError: division by zero
73
74 The error traceback now looks like::
75
76 ---------------------------------------------------------------------------
77 ZeroDivisionError Traceback (most recent call last)
78 Cell In [3], line 1
79 ----> 1 myfunc(2)
80
81 Cell In [2], line 2, in myfunc(z)
82 1 def myfunc(z):
83 ----> 2 foo.boo(z-1)
84
85 File ~/code/python/ipython/foo.py:3, in boo(x)
86 2 def boo(x):
87 ----> 3 return 1/(1-x)
88
89 ZeroDivisionError: division by zero
90
91 or, with xmode=Plain::
92
93 Traceback (most recent call last):
94 Cell In [12], line 1
95 myfunc(2)
96 Cell In [6], line 2 in myfunc
97 foo.boo(z-1)
98 File ~/code/python/ipython/foo.py:3 in boo
99 return 1/(1-x)
100 ZeroDivisionError: division by zero
101
102 :ghpull:`13560`
103
104 New setting to silence warning if working inside a virtual environment
105 ----------------------------------------------------------------------
106
107 Previously, when starting IPython in a virtual environment without IPython installed (so IPython from the global environment is used), the following warning was printed:
108
109 Attempting to work in a virtualenv. If you encounter problems, please install IPython inside the virtualenv.
110
111 This warning can be permanently silenced by setting ``c.InteractiveShell.warn_venv`` to ``False`` (the default is ``True``).
112
113 :ghpull:`13706`
114
115 -------
116
117 Thanks to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
118 work on IPython and related libraries.
119
120
5 121 .. _version 8.4.0:
6 122
7 123 IPython 8.4.0
@@ -39,7 +39,6 b' install_requires ='
39 39 pickleshare
40 40 prompt_toolkit>3.0.1,<3.1.0
41 41 pygments>=2.4.0
42 setuptools>=18.5
43 42 stack_data
44 43 traitlets>=5
45 44
@@ -80,7 +80,7 b' def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", p'
80 80 if pulls:
81 81 filtered = [ i for i in filtered if _parse_datetime(i['merged_at']) > since ]
82 82 # filter out PRs not against main (backports)
83 filtered = [ i for i in filtered if i['base']['ref'] == 'main' ]
83 filtered = [i for i in filtered if i["base"]["ref"] == "main"]
84 84 else:
85 85 filtered = [ i for i in filtered if not is_pull_request(i) ]
86 86
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now