55import os
66import sys
77import warnings
8+ import re
89from collections import OrderedDict
910
1011__escape_decoder = codecs .getdecoder ('unicode_escape' )
12+ __posix_variable = re .compile ('\$\{[^\}]*\}' )
1113
1214
1315def decode_escaped (escaped ):
@@ -21,7 +23,7 @@ def load_dotenv(dotenv_path):
2123 if not os .path .exists (dotenv_path ):
2224 warnings .warn ("Not loading %s - it doesn't exist." % dotenv_path )
2325 return None
24- for k , v in parse_dotenv (dotenv_path ):
26+ for k , v in dotenv_values (dotenv_path ). items ( ):
2527 os .environ .setdefault (k , v )
2628 return True
2729
@@ -36,7 +38,7 @@ def get_key(dotenv_path, key_to_get):
3638 if not os .path .exists (dotenv_path ):
3739 warnings .warn ("can't read %s - it doesn't exist." % dotenv_path )
3840 return None
39- dotenv_as_dict = OrderedDict ( parse_dotenv ( dotenv_path ) )
41+ dotenv_as_dict = dotenv_values ( dotenv_path )
4042 if key_to_get in dotenv_as_dict :
4143 return dotenv_as_dict [key_to_get ]
4244 else :
@@ -73,7 +75,7 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"):
7375 if not os .path .exists (dotenv_path ):
7476 warnings .warn ("can't delete from %s - it doesn't exist." % dotenv_path )
7577 return None , key_to_unset
76- dotenv_as_dict = OrderedDict ( parse_dotenv ( dotenv_path ) )
78+ dotenv_as_dict = dotenv_values ( dotenv_path )
7779 if key_to_unset in dotenv_as_dict :
7880 dotenv_as_dict .pop (key_to_unset , None )
7981 else :
@@ -83,6 +85,12 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"):
8385 return success , key_to_unset
8486
8587
88+ def dotenv_values (dotenv_path ):
89+ values = OrderedDict (parse_dotenv (dotenv_path ))
90+ values = resolve_nested_variables (values )
91+ return values
92+
93+
8694def parse_dotenv (dotenv_path ):
8795 with open (dotenv_path ) as f :
8896 for line in f :
@@ -103,6 +111,29 @@ def parse_dotenv(dotenv_path):
103111 yield k , v
104112
105113
114+ def resolve_nested_variables (values ):
115+ def _replacement (name ):
116+ """
117+ get appropiate value for a variable name.
118+ first search in environ, if not found,
119+ then look into the dotenv variables
120+ """
121+ ret = os .getenv (name , values .get (name , "" ))
122+ return ret
123+
124+ def _re_sub_callback (match_object ):
125+ """
126+ From a match object gets the variable name and returns
127+ the correct replacement
128+ """
129+ return _replacement (match_object .group ()[2 :- 1 ])
130+
131+ for k , v in values .items ():
132+ values [k ] = __posix_variable .sub (_re_sub_callback , v )
133+
134+ return values
135+
136+
106137def flatten_and_write (dotenv_path , dotenv_as_dict , quote_mode = "always" ):
107138 with open (dotenv_path , "w" ) as f :
108139 for k , v in dotenv_as_dict .items ():
0 commit comments