1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
import inspect
import io
import os
from dynaconf.utils import deduplicate
def _walk_to_root(path, break_at=None):
"""
Directories starting from the given directory up to the root or break_at
"""
if not os.path.exists(path): # pragma: no cover
raise IOError("Starting path not found")
if os.path.isfile(path): # pragma: no cover
path = os.path.dirname(path)
last_dir = None
current_dir = os.path.abspath(path)
paths = []
while last_dir != current_dir:
paths.append(current_dir)
paths.append(os.path.join(current_dir, "config"))
if break_at and current_dir == os.path.abspath(break_at): # noqa
break
parent_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir))
last_dir, current_dir = current_dir, parent_dir
return paths
SEARCHTREE = []
def find_file(filename=".env", project_root=None, skip_files=None, **kwargs):
"""Search in increasingly higher folders for the given file
Returns path to the file if found, or an empty string otherwise.
This function will build a `search_tree` based on:
- Project_root if specified
- Invoked script location and its parents until root
- Current working directory
For each path in the `search_tree` it will also look for an
aditional `./config` folder.
"""
search_tree = []
work_dir = os.getcwd()
skip_files = skip_files or []
# If filename is an absolute path and exists, just return it
# if the absolute path does not exist, return empty string so
# that it can be joined and avoid IoError
if os.path.isabs(filename):
return filename if os.path.exists(filename) else ""
if project_root is not None:
search_tree.extend(_walk_to_root(project_root, break_at=work_dir))
script_dir = os.path.dirname(os.path.abspath(inspect.stack()[-1].filename))
# Path to invoked script and recursively to root with its ./config dirs
search_tree.extend(_walk_to_root(script_dir))
# Path to where Python interpreter was invoked and recursively to root
search_tree.extend(_walk_to_root(work_dir))
# Don't look the same place twice
search_tree = deduplicate(search_tree)
global SEARCHTREE
SEARCHTREE[:] = search_tree
for dirname in search_tree:
check_path = os.path.join(dirname, filename)
if check_path in skip_files:
continue
if os.path.exists(check_path):
return check_path # First found will return
# return empty string if not found so it can still be joined in os.path
return ""
def read_file(path, **kwargs):
content = ""
with io.open(path, **kwargs) as open_file:
content = open_file.read().strip()
return content
def get_local_filename(filename):
"""Takes a filename like `settings.toml` and returns `settings.local.toml`
Arguments:
filename {str} -- The filename or complete path
Returns:
[str] -- The same name or path with `.local.` added.
"""
name, _, extension = os.path.basename(str(filename)).rpartition(
os.path.extsep
)
return os.path.join(
os.path.dirname(str(filename)), f"{name}.local.{extension}"
)
|