/usr/lib/python3/dist-packages/invoke/env.py is in python3-invoke 0.11.1+dfsg1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 108 109 110 111 112 113 114 115 | """
Environment variable configuration loading class.
Using a class here doesn't really model anything but makes state passing (in a
situation requiring it) more convenient.
This module is currently considered private/an implementation detail and should
not be included in the Sphinx API documentation.
"""
import os
import six
from .exceptions import UncastableEnvVar, AmbiguousEnvVar
from .util import debug
class Environment(object):
def __init__(self, config, prefix):
self._config = config
self._prefix = prefix
self.data = {} # Accumulator
def load(self):
"""
Return a nested dict containing values from `os.environ`.
Specifically, values whose keys map to already-known configuration
settings, allowing us to perform basic typecasting.
See :ref:`env-vars` for details.
"""
# Obtain allowed env var -> existing value map
env_vars = self._crawl(key_path=[], env_vars={})
m = "Scanning for env vars according to prefix: {1!r}, mapping: {0!r}"
debug(m.format(env_vars, self._prefix))
# Check for actual env var (honoring prefix) and try to set
for env_var, key_path in six.iteritems(env_vars):
real_var = (self._prefix or "") + env_var
if real_var in os.environ:
self._path_set(key_path, os.environ[real_var])
debug("Obtained env var config: {0!r}".format(self.data))
return self.data
def _crawl(self, key_path, env_vars):
"""
Examine config at location ``key_path`` & return potential env vars.
Uses ``env_vars`` dict to determine if a conflict exists, and raises an
exception if so. This dict is of the following form::
{
'EXPECTED_ENV_VAR_HERE': ['actual', 'nested', 'key_path'],
...
}
Returns another dictionary of new keypairs as per above.
"""
new_vars = {}
obj = self._path_get(key_path)
# Sub-dict -> recurse
if hasattr(obj, 'keys') and hasattr(obj, '__getitem__'):
for key in obj.keys():
merged_vars = dict(env_vars, **new_vars)
merged_path = key_path + [key]
crawled = self._crawl(merged_path, merged_vars)
# Handle conflicts
for key in crawled:
if key in new_vars:
err = "Found >1 source for {0}"
raise AmbiguousEnvVar(err.format(key))
# Merge and continue
new_vars.update(crawled)
# Other -> is leaf, no recursion
else:
new_vars[self._to_env_var(key_path)] = key_path
return new_vars
def _to_env_var(self, key_path):
return '_'.join(key_path).upper()
def _path_get(self, key_path):
# Gets are from self._config because that's what determines valid env
# vars and/or values for typecasting.
obj = self._config
for key in key_path:
obj = obj[key]
return obj
def _path_set(self, key_path, value):
# Sets are to self.data since that's what we are presenting to the
# outer config object and debugging.
obj = self.data
for key in key_path[:-1]:
if key not in obj:
obj[key] = {}
obj = obj[key]
old = self._path_get(key_path)
new_ = self._cast(old, value)
obj[key_path[-1]] = new_
def _cast(self, old, new_):
if isinstance(old, bool):
return new_ not in ('0', '')
elif isinstance(old, six.string_types):
return new_
elif old is None:
return new_
elif isinstance(old, (list, tuple)):
err = "Can't adapt an environment string into a {0}!"
err = err.format(type(old))
raise UncastableEnvVar(err)
else:
return old.__class__(new_)
|