17
17
import os
18
18
import re
19
19
import sys
20
+ import threading
20
21
import urllib .parse as urlparselib
21
22
import warnings
23
+ from os .path import expandvars
22
24
from urllib .parse import (
23
25
parse_qs ,
24
26
ParseResult ,
@@ -187,7 +189,10 @@ class Env:
187
189
}
188
190
CLOUDSQL = 'cloudsql'
189
191
192
+ VAR = re .compile (r'(?<!\\)\$\{?(?P<name>[A-Z_][0-9A-Z_]*)}?' , re .IGNORECASE )
193
+
190
194
def __init__ (self , ** scheme ):
195
+ self ._local = threading .local ()
191
196
self .smart_cast = True
192
197
self .escape_proxy = False
193
198
self .prefix = ""
@@ -358,9 +363,12 @@ def path(self, var, default=NOTSET, **kwargs):
358
363
"""
359
364
return Path (self .get_value (var , default = default ), ** kwargs )
360
365
361
- def get_value (self , var , cast = None , default = NOTSET , parse_default = False ):
366
+ def get_value (self , var , cast = None , default = NOTSET , parse_default = False , add_prefix = True ):
362
367
"""Return value for given environment variable.
363
368
369
+ - Substitute environment variable values.
370
+ - Detect infinite recursion in values (self-reference).
371
+
364
372
:param str var:
365
373
Name of variable.
366
374
:param collections.abc.Callable or None cast:
@@ -369,15 +377,30 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False):
369
377
If var not present in environ, return this instead.
370
378
:param bool parse_default:
371
379
Force to parse default.
380
+ :param bool add_prefix:
381
+ Whether to add prefix to variable name.
372
382
:returns: Value from environment or default (if set).
373
383
:rtype: typing.IO[typing.Any]
374
384
"""
375
385
386
+ var_name = "{}{}" .format (self .prefix , var ) if add_prefix else var
387
+ if not hasattr (self ._local , 'vars' ):
388
+ self ._local .vars = set ()
389
+ if var_name in self ._local .vars :
390
+ error_msg = "Environment variable '{}' recursively references itself (eventually)" .format (var_name )
391
+ raise ImproperlyConfigured (error_msg )
392
+
393
+ self ._local .vars .add (var_name )
394
+ try :
395
+ return self ._get_value (var_name , cast = cast , default = default , parse_default = parse_default )
396
+ finally :
397
+ self ._local .vars .remove (var_name )
398
+
399
+ def _get_value (self , var_name , cast = None , default = NOTSET , parse_default = False ):
376
400
logger .debug ("get '{}' casted as '{}' with default '{}'" .format (
377
- var , cast , default
401
+ var_name , cast , default
378
402
))
379
403
380
- var_name = "{}{}" .format (self .prefix , var )
381
404
if var_name in self .scheme :
382
405
var_info = self .scheme [var_name ]
383
406
@@ -403,26 +426,28 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False):
403
426
value = self .ENVIRON [var_name ]
404
427
except KeyError as exc :
405
428
if default is self .NOTSET :
406
- error_msg = "Set the {} environment variable" .format (var )
429
+ error_msg = "Set the {} environment variable" .format (var_name )
407
430
raise ImproperlyConfigured (error_msg ) from exc
408
431
409
432
value = default
410
433
434
+ # Substitute environment variables
435
+ if isinstance (value , (str , bytes )) and var_name != 'DJANGO_SECRET_KEY' :
436
+ def repl (m ):
437
+ return self .get_value (m ['name' ], cast = cast , default = default ,
438
+ parse_default = parse_default , add_prefix = False )
439
+ value = self .VAR .sub (repl , value )
440
+ value = expandvars (value )
441
+
411
442
# Resolve any proxied values
412
443
prefix = b'$' if isinstance (value , bytes ) else '$'
413
444
escape = rb'\$' if isinstance (value , bytes ) else r'\$'
414
- if hasattr (value , 'startswith' ) and value .startswith (prefix ):
415
- value = value .lstrip (prefix )
416
- value = self .get_value (value , cast = cast , default = default )
417
-
418
445
if self .escape_proxy and hasattr (value , 'replace' ):
419
446
value = value .replace (escape , prefix )
420
447
421
448
# Smart casting
422
- if self .smart_cast :
423
- if cast is None and default is not None and \
424
- not isinstance (default , NoValue ):
425
- cast = type (default )
449
+ if self .smart_cast and cast is None and default is not None and not isinstance (default , NoValue ):
450
+ cast = type (default )
426
451
427
452
value = None if default is None and value == '' else value
428
453
0 commit comments