Skip to content

Commit

Permalink
Improve dictionary completion performance.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jonathanslenders committed Jul 22, 2024
1 parent 79cb14b commit 3e7f68e
Showing 1 changed file with 22 additions and 8 deletions.
30 changes: 22 additions & 8 deletions ptpython/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,20 +476,34 @@ def _get_item_lookup_completions(
Complete dictionary keys.
"""

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

# We return a function, so that it gets computed when it's needed.
# When there are many completions, that improves the performance
# quite a bit (for the multi-column completion menu, we only need
# to display one meta text).
# Note that we also do the lookup itself in here (`obj[key]`),
# because this part can also be slow for some mapping
# implementations.
def get_value_repr() -> str:
text = self._do_repr(value)
nonlocal cached_result
if cached_result is not None:
return cached_result

try:
value = obj[key] # type: ignore

text = self._do_repr(value)
except BaseException:
return "-"

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

cached_result = text
return text

return get_value_repr
Expand All @@ -504,24 +518,24 @@ def get_value_repr() -> str:
# If this object is a dictionary, complete the keys.
if isinstance(result, (dict, collections_abc.Mapping)):
# Try to evaluate the key.
key_obj = key
key_obj_str = str(key)
for k in [key, key + '"', key + "'"]:
try:
key_obj = ast.literal_eval(k)
key_obj_str = str(ast.literal_eval(k))
except (SyntaxError, ValueError):
continue
else:
break

for k, v in result.items():
if str(k).startswith(str(key_obj)):
for k in result:
if str(k).startswith(key_obj_str):
try:
k_repr = self._do_repr(k)
yield Completion(
k_repr + "]",
-len(key),
display=f"[{k_repr}]",
display_meta=meta_repr(v),
display_meta=meta_repr(result, k),
)
except ReprFailedError:
pass
Expand All @@ -537,7 +551,7 @@ def get_value_repr() -> str:
k_repr + "]",
-len(key),
display=f"[{k_repr}]",
display_meta=meta_repr(result[k]),
display_meta=meta_repr(result, k),
)
except KeyError:
# `result[k]` lookup failed. Trying to complete
Expand Down

0 comments on commit 3e7f68e

Please sign in to comment.