from __future__ import absolute_import,print_function,unicode_literals
_E='.env'
_D='always'
_C=True
_B=False
_A=None
import io,logging,os,re,shutil,sys,tempfile
from collections import OrderedDict
from contextlib import contextmanager
from .compat import IS_TYPE_CHECKING,PY2,StringIO,to_env
from .parser import Binding,parse_stream
logger=logging.getLogger(__name__)
if IS_TYPE_CHECKING:
	from typing import Dict,Iterator,Match,Optional,Pattern,Union,Text,IO,Tuple
	if sys.version_info>=(3,6):_PathLike=os.PathLike
	else:_PathLike=Text
	if sys.version_info>=(3,0):_StringIO=StringIO
	else:_StringIO=StringIO[Text]
__posix_variable=re.compile('\n    \\$\\{\n        (?P<name>[^\\}:]*)\n        (?::-\n            (?P<default>[^\\}]*)\n        )?\n    \\}\n    ',re.VERBOSE)
def with_warn_for_invalid_lines(mappings):
	for A in mappings:
		if A.error:logger.warning('Python-dotenv could not parse statement starting at line %s',A.original.line)
		yield A
class DotEnv:
	def __init__(A,dotenv_path,verbose=_B,encoding=_A,interpolate=_C):A.dotenv_path=dotenv_path;A._dict=_A;A.verbose=verbose;A.encoding=encoding;A.interpolate=interpolate
	@contextmanager
	def _get_stream(self):
		A=self
		if isinstance(A.dotenv_path,StringIO):yield A.dotenv_path
		elif os.path.isfile(A.dotenv_path):
			with io.open(A.dotenv_path,encoding=A.encoding)as B:yield B
		else:
			if A.verbose:logger.info('Python-dotenv could not find configuration file %s.',A.dotenv_path or _E)
			yield StringIO('')
	def dict(A):
		if A._dict:return A._dict
		B=OrderedDict(A.parse());A._dict=resolve_nested_variables(B)if A.interpolate else B;return A._dict
	def parse(B):
		with B._get_stream()as C:
			for A in with_warn_for_invalid_lines(parse_stream(C)):
				if A.key is not _A:yield(A.key,A.value)
	def set_as_environment_variables(C,override=_B):
		for (A,B) in C.dict().items():
			if A in os.environ and not override:continue
			if B is not _A:os.environ[to_env(A)]=to_env(B)
		return _C
	def get(A,key):
		B=key;C=A.dict()
		if B in C:return C[B]
		if A.verbose:logger.warning('Key %s not found in %s.',B,A.dotenv_path)
		return _A
def get_key(dotenv_path,key_to_get):return DotEnv(dotenv_path,verbose=_C).get(key_to_get)
@contextmanager
def rewrite(path):
	try:
		with tempfile.NamedTemporaryFile(mode='w+',delete=_B)as A:
			with io.open(path)as B:yield(B,A)
	except BaseException:
		if os.path.isfile(A.name):os.unlink(A.name)
		raise
	else:shutil.move(A.name,path)
def set_key(dotenv_path,key_to_set,value_to_set,quote_mode=_D):
	K='"';E=quote_mode;C=dotenv_path;B=key_to_set;A=value_to_set;A=A.strip("'").strip(K)
	if not os.path.exists(C):logger.warning("Can't write to %s - it doesn't exist.",C);return _A,B,A
	if' 'in A:E=_D
	if E==_D:F='"{}"'.format(A.replace(K,'\\"'))
	else:F=A
	G='{}={}\n'.format(B,F)
	with rewrite(C)as(J,D):
		H=_B
		for I in with_warn_for_invalid_lines(parse_stream(J)):
			if I.key==B:D.write(G);H=_C
			else:D.write(I.original.string)
		if not H:D.write(G)
	return _C,B,A
def unset_key(dotenv_path,key_to_unset,quote_mode=_D):
	B=dotenv_path;A=key_to_unset
	if not os.path.exists(B):logger.warning("Can't delete from %s - it doesn't exist.",B);return _A,A
	C=_B
	with rewrite(B)as(E,F):
		for D in with_warn_for_invalid_lines(parse_stream(E)):
			if D.key==A:C=_C
			else:F.write(D.original.string)
	if not C:logger.warning("Key %s not removed from %s - key doesn't exist.",A,B);return _A,A
	return C,A
def resolve_nested_variables(values):
	def C(name,default):A=default;A=A if A is not _A else'';C=os.getenv(name,B.get(name,A));return C
	def D(match):A=match.groupdict();return C(name=A['name'],default=A['default'])
	B={}
	for (E,A) in values.items():B[E]=__posix_variable.sub(D,A)if A is not _A else _A
	return B
def _walk_to_root(path):
	A=path
	if not os.path.exists(A):raise IOError('Starting path not found')
	if os.path.isfile(A):A=os.path.dirname(A)
	C=_A;B=os.path.abspath(A)
	while C!=B:yield B;D=os.path.abspath(os.path.join(B,os.path.pardir));C,B=B,D
def find_dotenv(filename=_E,raise_error_if_not_found=_B,usecwd=_B):
	H='.py'
	def E():B='__file__';A=__import__('__main__',_A,_A,fromlist=[B]);return not hasattr(A,B)
	if usecwd or E()or getattr(sys,'frozen',_B):B=os.getcwd()
	else:
		A=sys._getframe()
		if PY2 and not __file__.endswith(H):C=__file__.rsplit('.',1)[0]+H
		else:C=__file__
		while A.f_code.co_filename==C:assert A.f_back is not _A;A=A.f_back
		F=A.f_code.co_filename;B=os.path.dirname(os.path.abspath(F))
	for G in _walk_to_root(B):
		D=os.path.join(G,filename)
		if os.path.isfile(D):return D
	if raise_error_if_not_found:raise IOError('File not found')
	return''
def load_dotenv(dotenv_path=_A,stream=_A,verbose=_B,override=_B,interpolate=_C,**A):B=dotenv_path or stream or find_dotenv();return DotEnv(B,verbose=verbose,interpolate=interpolate,**A).set_as_environment_variables(override=override)
def dotenv_values(dotenv_path=_A,stream=_A,verbose=_B,interpolate=_C,**A):B=dotenv_path or stream or find_dotenv();return DotEnv(B,verbose=verbose,interpolate=interpolate,**A).dict()