diff --git a/jedi/parser/utils.py b/jedi/parser/utils.py index 296c66f5..f74e3e63 100644 --- a/jedi/parser/utils.py +++ b/jedi/parser/utils.py @@ -10,6 +10,7 @@ import pickle from jedi import settings from jedi import debug +from jedi._compatibility import FileNotFoundError def underscore_memoization(func): @@ -128,12 +129,15 @@ class ParserPickling(object): # the pickle file is outdated return None - with open(self._get_hashed_path(grammar, path), 'rb') as f: - try: - gc.disable() - parser_cache_item = pickle.load(f) - finally: - gc.enable() + try: + with open(self._get_hashed_path(grammar, path), 'rb') as f: + try: + gc.disable() + parser_cache_item = pickle.load(f) + finally: + gc.enable() + except FileNotFoundError: + return None debug.dbg('pickle loaded: %s', path) parser_cache[path] = parser_cache_item diff --git a/test/test_cache.py b/test/test_cache.py index ba25cf84..56d3b804 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -3,6 +3,7 @@ Test all things related to the ``jedi.cache`` module. """ import time +from os import unlink import pytest @@ -65,6 +66,37 @@ def test_modulepickling_delete_incompatible_cache(): assert cached2 is None +@pytest.mark.usefixtures("isolated_jedi_cache") +def test_modulepickling_simulate_deleted_cache(): + """ + Tests loading from a cache file after it is deleted. + According to macOS `dev docs`__, + + Note that the system may delete the Caches/ directory to free up disk + space, so your app must be able to re-create or download these files as + needed. + + It is possible that other supported platforms treat cache files the same + way. + + __ https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html + """ + item = ParserCacheItem('fake parser') + path = 'fake path' + + cache = ParserPicklingCls() + cache.version = 1 + grammar = load_grammar() + cache.save_parser(grammar, path, item) + cached1 = load_stored_item(grammar, cache, path, item) + assert cached1 == item.parser + + unlink(cache._get_hashed_path(grammar, path)) + + cached2 = load_stored_item(grammar, cache, path, item) + assert cached2 is None + + @pytest.mark.skipif('True', message='Currently the star import cache is not enabled.') def test_star_import_cache_duration(): new = 0.01