Ejemplo n.º 1
0
    def load_from_data(self, data: Dict):
        """
        Load configuration data from readen files.
        """
        merged_data = config_merger.merge(dict(self.data.raw()), data)
        self.data = MigrationsDotty(merged_data)

        if self.env_key in self.data:
            env = self.data.pop(self.env_key)
            if env:
                for (name, value) in env.items():
                    os.environ[name] = value
    def test_compat_deep_merge(self, caplog: LogCaptureFixture):
        history = (PropertyMigration("some.namespace.old_property",
                                     "another.namespace.new_property"), )

        migrations.set_history(history)

        data = MigrationsDotty()
        data["another.namespace.new_property"] = "value"
        data["some.namespace.another"] = "foo"

        assert data.get("some.namespace.another") == "foo"
        assert data.get("some.namespace.old_property") == "value"
        assert data.get("another.namespace.new_property") == "value"

        assert data.get("some").get("namespace").get("another") == "foo"
        assert data.get("some").get("namespace").get("old_property") == "value"
        assert data.get("another").get("namespace").get(
            "new_property") == "value"

        logs = caplog.text

        assert re.match(
            r"WARNING .*\"some.namespace.old_property\" configuration property is deprecated and will be removed "
            r"in a future major release\. You should use \"another.namespace.new_property\" instead\.",
            logs)
    def test_warn_deprecated_simple(self, caplog: LogCaptureFixture):
        history = (PropertyMigration("old_property",
                                     "new_property",
                                     since="v1.1.0"), )

        migrations.set_history(history)

        data = MigrationsDotty({"old_property": "value"})

        migrations.migrate(data)

        assert data.get("old_property") == "value"
        assert data.get("new_property") == "value"

        logs = caplog.text

        assert re.match(
            r"WARNING .*\"old_property\" configuration property is deprecated since v1\.1\.0 and will be removed "
            r"in a future major release\. You should use \"new_property\" instead\.",
            logs)
Ejemplo n.º 4
0
 def __init__(self,
              paths: Union[ConfigPaths, None] = None,
              env_prefix='DDB',
              env_override_prefix='DDB_OVERRIDE',
              filenames=('ddb', 'ddb.local'),
              extensions=('yml', 'yaml'),
              cwd=None,
              args=None,
              unknown_args=None):
     self.env_prefix = env_prefix
     self.env_override_prefix = env_override_prefix
     self.filenames = filenames
     self.extensions = extensions
     self.env_additions = {}
     self.data = MigrationsDotty()
     self.paths = paths if paths else get_default_config_paths(
         env_prefix, filenames, extensions)
     self.cwd = cwd
     self.args = args if args else Namespace()
     self.unknown_args = unknown_args if unknown_args else []
     self.env_key = 'env'
Ejemplo n.º 5
0
class Config:  # pylint:disable=too-many-instance-attributes
    """
    Configuration
    """
    # Static default/overrides values for configuration, can be modified from code mainly for tests purpose
    defaults = None
    overrides = lambda config: config

    def __init__(self,
                 paths: Union[ConfigPaths, None] = None,
                 env_prefix='DDB',
                 env_override_prefix='DDB_OVERRIDE',
                 filenames=('ddb', 'ddb.local'),
                 extensions=('yml', 'yaml'),
                 cwd=None,
                 args=None,
                 unknown_args=None):
        self.env_prefix = env_prefix
        self.env_override_prefix = env_override_prefix
        self.filenames = filenames
        self.extensions = extensions
        self.env_additions = {}
        self.data = MigrationsDotty()
        self.paths = paths if paths else get_default_config_paths(
            env_prefix, filenames, extensions)
        self.cwd = cwd
        self.args = args if args else Namespace()
        self.unknown_args = unknown_args if unknown_args else []
        self.env_key = 'env'

    def reset(self, *args, **kwargs):
        """
        Reset the configuration object, while keeping configured paths.
        """
        self.__init__(*args, paths=self.paths, **kwargs)

    def clear(self):
        """
        Remove all configuration data.
        """
        self.data.clear()
        self.env_additions.clear()

    @property
    def project_configuration_file(self):
        """
        Retrieve the project configuration file.
        """
        return configuration_file(self.paths.project_home, self.filenames,
                                  self.extensions)

    @property
    def project_cwd(self):
        """
        Retrieve the current working directory, relative to project_home.
        :return:
        """
        if self.paths.project_home and self.cwd:
            return os.path.relpath(self.cwd, self.paths.project_home)
        return None

    @property
    def eject(self):
        """
        Check if command line is running configure command with --eject flag.
        :return:
        """
        return 'command' in self.args and self.args.command == 'configure' and \
               'eject' in self.args and self.args.eject

    @property
    def clear_cache(self):
        """
        Should cache be cleared on load
        """
        if 'clear_cache' in self.args:
            return self.args.clear_cache
        return False

    @property
    def files(self):
        """
        Possible configuration files to load.
        """
        ret = []
        for path in self.paths:
            if not path:
                continue
            for basename in self.filenames:
                for ext in self.extensions:
                    if basename.endswith('.' + ext) and os.path.exists(
                            os.path.join(path, basename)):
                        file = os.path.join(path, basename)
                    else:
                        file = os.path.join(path, basename + '.' + ext)
                    ret.append(file)
        return ret

    def read(self, defaults=None, files=None):
        """
        Read configuration data from files.
        """
        if defaults is None:
            loaded_data = {}
        else:
            loaded_data = dict(defaults)

        if files is None:
            files = self.files

        found_files = {}

        for file in files:
            if exists(file):
                with open(file, 'rb') as stream:
                    file_data = yaml.safe_load(stream)
                    found_files[file] = self.apply_environ_overrides(
                        deepcopy(file_data))
                    if file_data:
                        loaded_data = config_merger.merge(
                            loaded_data, file_data)

        if Config.overrides:  # pylint:disable=using-constant-test
            Config.overrides(loaded_data)

        return self.apply_environ_overrides(loaded_data), found_files

    def load_from_data(self, data: Dict):
        """
        Load configuration data from readen files.
        """
        merged_data = config_merger.merge(dict(self.data.raw()), data)
        self.data = MigrationsDotty(merged_data)

        if self.env_key in self.data:
            env = self.data.pop(self.env_key)
            if env:
                for (name, value) in env.items():
                    os.environ[name] = value

    def load(self, defaults=None, files=None):
        """
        Load configuration data from files. Variable in 'env_key' key will be placed loaded as environment variables.
        """
        defaults_to_apply = {} if Config.defaults is None else dict(
            Config.defaults)
        if defaults:
            # Config.defaults should have the priority over given defaults.
            defaults_to_apply = config_merger.merge(defaults,
                                                    defaults_to_apply)
        read_data, _ = self.read(defaults_to_apply, files)
        self.load_from_data(read_data)

    def apply_environ_overrides(self, data, prefix=None):
        """
        Apply environment variables to configuration.
        """
        if not prefix:
            prefix = self.env_override_prefix
        prefix = prefix.upper()

        environ_value = os.environ.get(prefix)
        if environ_value:
            if environ_value.lower() == str(True):
                environ_value = True
            elif environ_value.lower() == str(False):
                environ_value = False
            elif environ_value.isdigit():
                environ_value = int(environ_value)
            if isinstance(data, bool):
                environ_value = bool(environ_value)
            return environ_value

        if isinstance(data, dict):
            for (name, value) in data.items():
                key_prefix = prefix + "_" + name
                key_prefix = key_prefix.upper()

                data[name] = self.apply_environ_overrides(value, key_prefix)
        if isinstance(data, list):
            i = 0
            for value in data:
                replace_prefix = prefix + "[" + str(i) + "]"
                replace_prefix = replace_prefix.upper()

                environ_value = os.environ.get(replace_prefix)
                if environ_value:
                    data[i] = self.apply_environ_overrides(
                        value, replace_prefix)

                i += 1

        return data