This file is indexed.

/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_)