1
0
forked from VimPlug/jedi
Files
jedi-fork/jedi/parser/utils.py
2017-03-30 01:53:18 +02:00

129 lines
3.4 KiB
Python

import time
import os
import sys
import hashlib
import gc
import shutil
import pickle
import platform
from jedi import settings
from jedi import debug
from jedi._compatibility import FileNotFoundError
_PICKLE_VERSION = 30
"""
Version number (integer) for file system cache.
Increment this number when there are any incompatible changes in
the parser tree classes. For example, the following changes
are regarded as incompatible.
- A class name is changed.
- A class is moved to another module.
- A __slot__ of a class is changed.
"""
_VERSION_TAG = '%s-%s%s-%s' % (
platform.python_implementation(),
sys.version_info[0],
sys.version_info[1],
_PICKLE_VERSION
)
"""
Short name for distinguish Python implementations and versions.
It's like `sys.implementation.cache_tag` but for Python < 3.3
we generate something similar. See:
http://docs.python.org/3/library/sys.html#sys.implementation
"""
# for fast_parser, should not be deleted
parser_cache = {}
class _NodeCacheItem(object):
def __init__(self, node, lines, change_time=None):
self.node = node
self.lines = lines
if change_time is None:
change_time = time.time()
self.change_time = change_time
def load_module(grammar, path):
"""
Returns a module or None, if it fails.
"""
p_time = os.path.getmtime(path)
try:
# TODO Add grammar sha256
module_cache_item = parser_cache[path]
if p_time <= module_cache_item.change_time:
return module_cache_item.node
except KeyError:
if not settings.use_filesystem_cache:
return None
cache_path = _get_hashed_path(grammar, path)
try:
if p_time > os.path.getmtime(cache_path):
# Cache is outdated
return None
with open(cache_path, 'rb') as f:
gc.disable()
try:
module_cache_item = pickle.load(f)
finally:
gc.enable()
except FileNotFoundError:
return None
else:
parser_cache[path] = module_cache_item
debug.dbg('pickle loaded: %s', path)
return module_cache_item.node
def save_module(grammar, path, module, lines, pickling=True):
try:
p_time = None if path is None else os.path.getmtime(path)
except OSError:
p_time = None
pickling = False
item = _NodeCacheItem(module, lines, p_time)
parser_cache[path] = item
if settings.use_filesystem_cache and pickling and path is not None:
with open(_get_hashed_path(grammar, path), 'wb') as f:
pickle.dump(item, f, pickle.HIGHEST_PROTOCOL)
def remove_old_modules(self):
"""
# TODO Might want to use such a function to clean up the cache (if it's
# too old). We could potentially also scan for old files in the
# directory and delete those.
"""
def clear_cache(self):
shutil.rmtree(settings.cache_directory)
parser_cache.clear()
def _get_hashed_path(grammar, path):
file_hash = hashlib.sha256(path.encode("utf-8")).hexdigest()
directory = _get_cache_directory_path()
return os.path.join(directory, '%s-%s.pkl' % (grammar.sha256, file_hash))
def _get_cache_directory_path():
directory = os.path.join(settings.cache_directory, _VERSION_TAG)
if not os.path.exists(directory):
os.makedirs(directory)
return directory