diff --git a/news/commands_cache_fix_update.rst b/news/commands_cache_fix_update.rst new file mode 100644 index 000000000..3c8ad1d55 --- /dev/null +++ b/news/commands_cache_fix_update.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Commands Cache: Fixed cache update logic that lead to lagging during typing. + +**Security:** + +* diff --git a/tests/test_commands_cache.py b/tests/test_commands_cache.py index a586b9b02..bf3012dbd 100644 --- a/tests/test_commands_cache.py +++ b/tests/test_commands_cache.py @@ -187,9 +187,16 @@ def test_update_cache(xession, tmp_path): file1.touch() file1.chmod(0o755) - cache = CommandsCache({"PATH": [subdir2, subdir1]}) + paths = [subdir2, subdir1] + cache = CommandsCache({"PATH": paths}) cached = cache.update_cache() + # Check there are no changes after update cache. + c1 = cache._update_and_check_changes(paths) + c2 = cache._update_and_check_changes(paths) + c3 = cache._update_and_check_changes(paths) + assert [c1, c2, c3] == [True, False, False] + assert file1.samefile(cached[basename][0]) # give the os enough time to update the mtime field of the parent directory diff --git a/xonsh/commands_cache.py b/xonsh/commands_cache.py index 07cc046c1..8abdfb330 100644 --- a/xonsh/commands_cache.py +++ b/xonsh/commands_cache.py @@ -119,14 +119,21 @@ class CommandsCache(cabc.Mapping): if os.path.isdir(p): yield p - def _check_changes(self, paths: tuple[str, ...]): - # did PATH change? - yield self._update_paths_cache(paths) + def _update_aliases_cache(self): + """Update aliases checksum and return result: updated or not.""" + prev_hash = self._alias_checksum + self._alias_checksum = hash(frozenset(self.aliases)) + return prev_hash != self._alias_checksum - # did aliases change? - al_hash = hash(frozenset(self.aliases)) - yield al_hash != self._alias_checksum - self._alias_checksum = al_hash + def _update_and_check_changes(self, paths: tuple[str, ...]): + """Update cache and return the result: updated or still the same. + + Be careful in this place. Both `_update_*` functions must be called + because they are changing state after update. + """ + is_aliases_change = self._update_aliases_cache() + is_paths_change = self._update_paths_cache(paths) + return is_aliases_change or is_paths_change @property def all_commands(self): @@ -159,7 +166,7 @@ class CommandsCache(cabc.Mapping): # iterate backwards so that entries at the front of PATH overwrite # entries at the back. paths = tuple(reversed(tuple(self.remove_dups(env.get("PATH") or [])))) - if any(self._check_changes(paths)): + if self._update_and_check_changes(paths): all_cmds = CacheDict() for cmd, path in self._iter_binaries(paths): # None -> not in aliases