93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
|
# This file is part of the django-environ.
|
||
|
#
|
||
|
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
|
||
|
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
|
||
|
#
|
||
|
# For the full copyright and license information, please view
|
||
|
# the LICENSE.txt file that was distributed with this source code.
|
||
|
|
||
|
"""Docker-style file variable support module."""
|
||
|
|
||
|
import os
|
||
|
from collections.abc import MutableMapping
|
||
|
|
||
|
|
||
|
class FileAwareMapping(MutableMapping):
|
||
|
"""
|
||
|
A mapping that wraps os.environ, first checking for the existence of a key
|
||
|
appended with ``_FILE`` whenever reading a value. If a matching file key is
|
||
|
found then the value is instead read from the file system at this location.
|
||
|
|
||
|
By default, values read from the file system are cached so future lookups
|
||
|
do not hit the disk again.
|
||
|
|
||
|
A ``_FILE`` key has higher precedence than a value is set directly in the
|
||
|
environment, and an exception is raised if the file can not be found.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, env=None, cache=True):
|
||
|
"""
|
||
|
Initialize the mapping.
|
||
|
|
||
|
:param env:
|
||
|
where to read environment variables from (defaults to
|
||
|
``os.environ``)
|
||
|
:param cache:
|
||
|
cache environment variables read from the file system (defaults to
|
||
|
``True``)
|
||
|
"""
|
||
|
self.env = env if env is not None else os.environ
|
||
|
self.cache = cache
|
||
|
self.files_cache = {}
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
if self.cache and key in self.files_cache:
|
||
|
return self.files_cache[key]
|
||
|
key_file = self.env.get(key + "_FILE")
|
||
|
if key_file:
|
||
|
with open(key_file) as f:
|
||
|
value = f.read()
|
||
|
if self.cache:
|
||
|
self.files_cache[key] = value
|
||
|
return value
|
||
|
return self.env[key]
|
||
|
|
||
|
def __iter__(self):
|
||
|
"""
|
||
|
Iterate all keys, also always including the shortened key if ``_FILE``
|
||
|
keys are found.
|
||
|
"""
|
||
|
for key in self.env:
|
||
|
yield key
|
||
|
if key.endswith("_FILE"):
|
||
|
no_file_key = key[:-5]
|
||
|
if no_file_key and no_file_key not in self.env:
|
||
|
yield no_file_key
|
||
|
|
||
|
def __len__(self):
|
||
|
"""
|
||
|
Return the length of the file, also always counting shortened keys for
|
||
|
any ``_FILE`` key found.
|
||
|
"""
|
||
|
return len(tuple(iter(self)))
|
||
|
|
||
|
def __setitem__(self, key, value):
|
||
|
self.env[key] = value
|
||
|
if self.cache and key.endswith("_FILE"):
|
||
|
no_file_key = key[:-5]
|
||
|
if no_file_key and no_file_key in self.files_cache:
|
||
|
del self.files_cache[no_file_key]
|
||
|
|
||
|
def __delitem__(self, key):
|
||
|
file_key = key + "_FILE"
|
||
|
if file_key in self.env:
|
||
|
del self[file_key]
|
||
|
if key in self.env:
|
||
|
del self.env[key]
|
||
|
return
|
||
|
if self.cache and key.endswith("_FILE"):
|
||
|
no_file_key = key[:-5]
|
||
|
if no_file_key and no_file_key in self.files_cache:
|
||
|
del self.files_cache[no_file_key]
|
||
|
del self.env[key]
|