_B=False
_A=None
import time,os,sys,hashlib,gc,shutil,platform,logging,warnings,pickle
from pathlib import Path
from typing import Dict,Any
LOG=logging.getLogger(__name__)
_CACHED_FILE_MINIMUM_SURVIVAL=600
'\nCached files should survive at least a few minutes.\n'
_CACHED_FILE_MAXIMUM_SURVIVAL=2592000
'\nMaximum time for a cached file to survive if it is not\naccessed within.\n'
_CACHED_SIZE_TRIGGER=600
"\nThis setting limits the amount of cached files. It's basically a way to start\ngarbage collection.\n\nThe reasoning for this limit being as big as it is, is the following:\n\nNumpy, Pandas, Matplotlib and Tensorflow together use about 500 files. This\nmakes Jedi use ~500mb of memory. Since we might want a bit more than those few\nlibraries, we just increase it a bit.\n"
_PICKLE_VERSION=33
'\nVersion number (integer) for file system cache.\n\nIncrement this number when there are any incompatible changes in\nthe parser tree classes.  For example, the following changes\nare regarded as incompatible.\n\n- A class name is changed.\n- A class is moved to another module.\n- A __slot__ of a class is changed.\n'
_VERSION_TAG='%s-%s%s-%s'%(platform.python_implementation(),sys.version_info[0],sys.version_info[1],_PICKLE_VERSION)
"\nShort name for distinguish Python implementations and versions.\n\nIt's a bit similar to `sys.implementation.cache_tag`.\nSee: http://docs.python.org/3/library/sys.html#sys.implementation\n"
def _get_default_cache_path():
	A='Parso'
	if platform.system().lower()=='windows':B=Path(os.getenv('LOCALAPPDATA')or'~',A,A)
	elif platform.system().lower()=='darwin':B=Path('~','Library','Caches',A)
	else:B=Path(os.getenv('XDG_CACHE_HOME')or'~/.cache','parso')
	return B.expanduser()
_default_cache_path=_get_default_cache_path()
'\nThe path where the cache is stored.\n\nOn Linux, this defaults to ``~/.cache/parso/``, on OS X to\n``~/Library/Caches/Parso/`` and on Windows to ``%LOCALAPPDATA%\\Parso\\Parso\\``.\nOn Linux, if environment variable ``$XDG_CACHE_HOME`` is set,\n``$XDG_CACHE_HOME/parso`` is used instead of the default one.\n'
_CACHE_CLEAR_THRESHOLD=86400
def _get_cache_clear_lock_path(cache_path=_A):'\n    The path where the cache lock is stored.\n\n    Cache lock will prevent continous cache clearing and only allow garbage\n    collection once a day (can be configured in _CACHE_CLEAR_THRESHOLD).\n    ';A=cache_path;A=A or _default_cache_path;return A.joinpath('PARSO-CACHE-LOCK')
parser_cache={}
class _NodeCacheItem:
	def __init__(A,node,lines,change_time=_A):
		B=change_time;A.node=node;A.lines=lines
		if B is _A:B=time.time()
		A.change_time=B;A.last_used=B
def load_module(hashed_grammar,file_io,cache_path=_A):
	'\n    Returns a module or None, if it fails.\n    ';D=hashed_grammar;A=file_io;B=A.get_last_modified()
	if B is _A:return
	try:
		C=parser_cache[D][A.path]
		if B<=C.change_time:C.last_used=time.time();return C.node
	except KeyError:return _load_from_file_system(D,A.path,B,cache_path=cache_path)
def _load_from_file_system(hashed_grammar,path,p_time,cache_path=_A):
	C=hashed_grammar;B=path;A=cache_path;A=_get_hashed_path(C,B,cache_path=A)
	try:
		if p_time>os.path.getmtime(A):return
		with open(A,'rb')as E:
			gc.disable()
			try:D=pickle.load(E)
			finally:gc.enable()
	except FileNotFoundError:return
	else:_set_cache_item(C,B,D);LOG.debug('pickle loaded: %s',B);return D.node
def _set_cache_item(hashed_grammar,path,module_cache_item):
	if sum(len(A)for A in parser_cache.values())>=_CACHED_SIZE_TRIGGER:
		B=time.time()-_CACHED_FILE_MINIMUM_SURVIVAL
		for(A,C)in parser_cache.items():parser_cache[A]={C:A for(C,A)in C.items()if A.last_used>B}
	parser_cache.setdefault(hashed_grammar,{})[path]=module_cache_item
def try_to_save_module(hashed_grammar,file_io,module,lines,pickling=True,cache_path=_A):
	B=cache_path;C=pickling;D=file_io;E=hashed_grammar;A=D.path
	try:F=_A if A is _A else D.get_last_modified()
	except OSError:F=_A;C=_B
	G=_NodeCacheItem(module,lines,F);_set_cache_item(E,A,G)
	if C and A is not _A:
		try:_save_to_file_system(E,A,G,cache_path=B)
		except PermissionError:warnings.warn('Tried to save a file to %s, but got permission denied.'%A,Warning)
		else:_remove_cache_and_update_lock(cache_path=B)
def _save_to_file_system(hashed_grammar,path,item,cache_path=_A):
	with open(_get_hashed_path(hashed_grammar,path,cache_path=cache_path),'wb')as A:pickle.dump(item,A,pickle.HIGHEST_PROTOCOL)
def clear_cache(cache_path=_A):
	A=cache_path
	if A is _A:A=_default_cache_path
	shutil.rmtree(A);parser_cache.clear()
def clear_inactive_cache(cache_path=_A,inactivity_threshold=_CACHED_FILE_MAXIMUM_SURVIVAL):
	A=cache_path
	if A is _A:A=_default_cache_path
	if not A.exists():return _B
	for D in os.listdir(A):
		B=A.joinpath(D)
		if not B.is_dir():continue
		for C in os.scandir(B):
			if C.stat().st_atime+_CACHED_FILE_MAXIMUM_SURVIVAL<=time.time():
				try:os.remove(C.path)
				except OSError:continue
	else:return True
def _touch(path):
	try:os.utime(path,_A)
	except FileNotFoundError:
		try:A=open(path,'a');A.close()
		except(OSError,IOError):return _B
	return True
def _remove_cache_and_update_lock(cache_path=_A):
	B=cache_path;C=_get_cache_clear_lock_path(cache_path=B)
	try:A=os.path.getmtime(C)
	except FileNotFoundError:A=_A
	if A is _A or A+_CACHE_CLEAR_THRESHOLD<=time.time():
		if not _touch(C):return _B
		clear_inactive_cache(cache_path=B)
def _get_hashed_path(hashed_grammar,path,cache_path=_A):A=_get_cache_directory_path(cache_path=cache_path);B=hashlib.sha256(str(path).encode('utf-8')).hexdigest();return os.path.join(A,'%s-%s.pkl'%(hashed_grammar,B))
def _get_cache_directory_path(cache_path=_A):
	A=cache_path
	if A is _A:A=_default_cache_path
	B=A.joinpath(_VERSION_TAG)
	if not B.exists():os.makedirs(B)
	return B