Skip to content

Commit 3e7f68e

Browse files
Improve dictionary completion performance.
This improves the performance for dictionary-like objects where iterating over the keys is fast, but doing a lookup for the values is slow. This change ensures we only do value lookups when really needed. The change also caches the meta text so that we don't have to recompute it during navigation of the completion menu.
1 parent 79cb14b commit 3e7f68e

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

ptpython/completer.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -476,20 +476,34 @@ def _get_item_lookup_completions(
476476
Complete dictionary keys.
477477
"""
478478

479-
def meta_repr(value: object) -> Callable[[], str]:
479+
def meta_repr(obj: object, key: object) -> Callable[[], str]:
480480
"Abbreviate meta text, make sure it fits on one line."
481+
cached_result: str | None = None
481482

482483
# We return a function, so that it gets computed when it's needed.
483484
# When there are many completions, that improves the performance
484485
# quite a bit (for the multi-column completion menu, we only need
485486
# to display one meta text).
487+
# Note that we also do the lookup itself in here (`obj[key]`),
488+
# because this part can also be slow for some mapping
489+
# implementations.
486490
def get_value_repr() -> str:
487-
text = self._do_repr(value)
491+
nonlocal cached_result
492+
if cached_result is not None:
493+
return cached_result
494+
495+
try:
496+
value = obj[key] # type: ignore
497+
498+
text = self._do_repr(value)
499+
except BaseException:
500+
return "-"
488501

489502
# Take first line, if multiple lines.
490503
if "\n" in text:
491504
text = text.split("\n", 1)[0] + "..."
492505

506+
cached_result = text
493507
return text
494508

495509
return get_value_repr
@@ -504,24 +518,24 @@ def get_value_repr() -> str:
504518
# If this object is a dictionary, complete the keys.
505519
if isinstance(result, (dict, collections_abc.Mapping)):
506520
# Try to evaluate the key.
507-
key_obj = key
521+
key_obj_str = str(key)
508522
for k in [key, key + '"', key + "'"]:
509523
try:
510-
key_obj = ast.literal_eval(k)
524+
key_obj_str = str(ast.literal_eval(k))
511525
except (SyntaxError, ValueError):
512526
continue
513527
else:
514528
break
515529

516-
for k, v in result.items():
517-
if str(k).startswith(str(key_obj)):
530+
for k in result:
531+
if str(k).startswith(key_obj_str):
518532
try:
519533
k_repr = self._do_repr(k)
520534
yield Completion(
521535
k_repr + "]",
522536
-len(key),
523537
display=f"[{k_repr}]",
524-
display_meta=meta_repr(v),
538+
display_meta=meta_repr(result, k),
525539
)
526540
except ReprFailedError:
527541
pass
@@ -537,7 +551,7 @@ def get_value_repr() -> str:
537551
k_repr + "]",
538552
-len(key),
539553
display=f"[{k_repr}]",
540-
display_meta=meta_repr(result[k]),
554+
display_meta=meta_repr(result, k),
541555
)
542556
except KeyError:
543557
# `result[k]` lookup failed. Trying to complete

0 commit comments

Comments
 (0)