Example #1
0
def main(args):
    # Build configuration object just like we do in Antenna.
    config = ConfigManager([
        # Pull configuration from env file specified as ANTENNA_ENV
        ConfigEnvFileEnv([os.environ.get('ANTENNA_ENV')]),

        # Pull configuration from environment variables
        ConfigOSEnv()
    ])

    # We create it in the crashstorage namespace because that's how Antenna
    # uses it. This makes it easier to use existing configuration.
    conn = S3Connection(config.with_namespace('crashstorage'), no_verify=True)

    # First, check to see if the bucket is already created.
    try:
        print('Checking to see if bucket "%s" exists...' % conn.bucket)
        conn.verify_configuration()
        print('Bucket exists.')

    except ClientError as exc:
        print(str(exc))
        if 'HeadBucket operation: Not Found' in str(exc):
            print('Bucket not found. Creating %s ...' % conn.bucket)
            conn._create_bucket()
            print('Bucket created.')
        else:
            raise
Example #2
0
def test_with_options():
    """Verify .with_options() restricts configuration"""
    config = ConfigManager([
        ConfigDictEnv({
            "FOO_BAR": "a",
            "FOO_BAZ": "b",
            "BAR": "c",
            "BAZ": "d"
        })
    ])

    class SomeComponent(RequiredConfigMixin):
        required_config = ConfigOptions()
        required_config.add_option("baz",
                                   default="",
                                   doc="some help here",
                                   parser=str)

        def __init__(self, config):
            self.config = config.with_options(self)

    # Create the component with regular config
    comp = SomeComponent(config)
    assert comp.config("baz") == "d"
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp.config("bar")

    # Create the component with config in the "foo" namespace
    comp2 = SomeComponent(config.with_namespace("foo"))
    assert comp2.config("baz") == "b"
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp2.config("bar")
Example #3
0
def main(args):
    # Build configuration object just like we do in Antenna.
    config = ConfigManager([
        # Pull configuration from env file specified as ANTENNA_ENV
        ConfigEnvFileEnv([os.environ.get('ANTENNA_ENV')]),

        # Pull configuration from environment variables
        ConfigOSEnv()
    ])

    # We create it in the crashstorage namespace because that's how Antenna
    # uses it. This makes it easier to use existing configuration.
    conn = S3Connection(config.with_namespace('crashstorage'), no_verify=True)

    # First, check to see if the bucket is already created.
    try:
        print('Checking to see if bucket "%s" exists...' % conn.bucket)
        conn.verify_configuration()
        print('Bucket exists.')

    except ClientError as exc:
        print(str(exc))
        if 'HeadBucket operation: Not Found' in str(exc):
            print('Bucket not found. Creating %s ...' % conn.bucket)
            # Create the bucket.
            conn._create_bucket()
            print('Bucket created.')
        else:
            raise
Example #4
0
def test_with_options():
    """Verify .with_options() restricts configuration"""
    config = ConfigManager([
        ConfigDictEnv({
            'FOO_BAR': 'a',
            'FOO_BAZ': 'b',
            'BAR': 'c',
            'BAZ': 'd',
        })
    ])

    class SomeComponent(RequiredConfigMixin):
        required_config = ConfigOptions()
        required_config.add_option('baz',
                                   default='',
                                   doc='some help here',
                                   parser=str)

        def __init__(self, config):
            self.config = config.with_options(self)

    # Create the component with regular config
    comp = SomeComponent(config)
    assert comp.config('baz') == 'd'
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp.config('bar')

    # Create the component with config in the "foo" namespace
    comp2 = SomeComponent(config.with_namespace('foo'))
    assert comp2.config('baz') == 'b'
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp2.config('bar')
def main(args):
    # Build configuration object just like we do in Antenna.
    config = ConfigManager([
        # Pull configuration from environment variables
        ConfigOSEnv()
    ])

    # We create it in the crashstorage namespace because that's how Antenna
    # uses it. This makes it easier to use existing configuration.
    conn = S3Connection(config.with_namespace('crashstorage'))

    # First, check to see if the bucket is already created.
    try:
        print('Checking to see if bucket "%s" exists...' % conn.bucket)
        conn.verify_write_to_bucket()
        print('Bucket exists.')

    except ClientError as exc:
        print(str(exc))
        if '(NoSuchBucket)' in str(exc):
            print('Bucket not found. Creating %s ...' % conn.bucket)
            conn.client.create_bucket(Bucket=conn.bucket)
            print('Bucket created.')
        else:
            raise
Example #6
0
def test_dummy():
    config = ConfigManager([
        ConfigYamlEnv('my.yaml'),
        ConfigOSEnv(),
        ConfigDictEnv({'COMESFROMDICT': 'comes from dict'})
    ])
    assert config('comesfromyaml') == 'comes from yaml'
    assert config('THIS_COMES_FROM_YAML') == 'too'
    assert config.with_namespace('this').with_namespace(
        'comes').with_namespace('from')('yaml') == 'too'
    assert config('USER') == 'aagibalov'
    assert config('comesfromdict') == 'comes from dict'

    try:
        config('SomeSecret')
        pytest.fail()
    except everett.ConfigurationError:
        pass

    assert config('SomeSecret', default='SomeDefault') == 'SomeDefault'
Example #7
0
def test_with_namespace():
    config = ConfigManager(
        [ConfigDictEnv({
            'FOO_BAR': 'foobaz',
            'BAR': 'baz',
            'BAT': 'bat',
        })])

    # Verify the values first
    assert config('bar', namespace=['foo']) == 'foobaz'
    assert config('bar') == 'baz'
    assert config('bat') == 'bat'

    # Create the namespaced config
    config_with_namespace = config.with_namespace('foo')
    assert config_with_namespace('bar') == 'foobaz'

    # Verify 'bat' is not available because it's not in the namespace
    with pytest.raises(ConfigurationError):
        config_with_namespace('bat')
Example #8
0
def test_with_namespace():
    config = ConfigManager(
        [ConfigDictEnv({
            "FOO_BAR": "foobaz",
            "BAR": "baz",
            "BAT": "bat"
        })])

    # Verify the values first
    assert config("bar", namespace=["foo"]) == "foobaz"
    assert config("bar") == "baz"
    assert config("bat") == "bat"

    # Create the namespaced config
    config_with_namespace = config.with_namespace("foo")
    assert config_with_namespace("bar") == "foobaz"

    # Verify 'bat' is not available because it's not in the namespace
    with pytest.raises(ConfigurationError):
        config_with_namespace("bat")
Example #9
0
def test_with_namespace():
    config = ConfigManager([
        ConfigDictEnv({
            'FOO_BAR': 'foobaz',
            'BAR': 'baz',
            'BAT': 'bat',
        })
    ])

    # Verify the values first
    assert config('bar', namespace=['foo']) == 'foobaz'
    assert config('bar') == 'baz'
    assert config('bat') == 'bat'

    # Create the namespaced config
    config_with_namespace = config.with_namespace('foo')
    assert config_with_namespace('bar') == 'foobaz'

    # Verify 'bat' is not available because it's not in the namespace
    with pytest.raises(ConfigurationError):
        config_with_namespace('bat')
Example #10
0
def test_with_options():
    """Verify .with_options() restricts configuration"""
    config = ConfigManager([
        ConfigDictEnv({
            'FOO_BAR': 'a',
            'FOO_BAZ': 'b',

            'BAR': 'c',
            'BAZ': 'd',
        })
    ])

    class SomeComponent(RequiredConfigMixin):
        required_config = ConfigOptions()
        required_config.add_option(
            'baz',
            default='',
            doc='some help here',
            parser=str
        )

        def __init__(self, config):
            self.config = config.with_options(self)

    # Create the component with regular config
    comp = SomeComponent(config)
    assert comp.config('baz') == 'd'
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp.config('bar')

    # Create the component with config in the "foo" namespace
    comp2 = SomeComponent(config.with_namespace('foo'))
    assert comp2.config('baz') == 'b'
    with pytest.raises(ConfigurationError):
        # This is not a valid option for this component
        comp2.config('bar')
Example #11
0
def get_program_config():
    sources = [ConfigOSEnv()]
    if "RS_CONFIG_FILE" in os.environ:
        sources.append(ConfigYamlEnv(os.environ["RS_CONFIG_FILE"]))
    config = ConfigManager(sources)
    return MainConfig(config.with_namespace("rs"))
Example #12
0
class RVConfig:
    """A store of global user-specific configuration not tied to particular pipelines.

    This is used to store user-specific configuration like the root temporary
    directory, verbosity, and other system-wide configuration handled by Everett
    (eg. which AWS Batch job queue to use).

    Attributes:
        DEFAULT_PROFILE: the default RV configuration profile name
        DEFAULT_TMP_DIR_ROOT: the default location for root of temporary directories
    """
    DEFAULT_PROFILE: str = 'default'
    DEFAULT_TMP_DIR_ROOT: str = '/opt/data/tmp'

    def __init__(self):
        self.set_verbosity()
        self.set_tmp_dir_root()
        self.set_everett_config()

    def set_verbosity(self, verbosity: Verbosity = Verbosity.NORMAL):
        """Set verbosity level for logging."""
        self.verbosity = verbosity
        root_log = logging.getLogger('rastervision2')
        if self.verbosity >= Verbosity.VERBOSE:
            root_log.setLevel(logging.DEBUG)
        elif self.verbosity >= Verbosity.NORMAL:
            root_log.setLevel(logging.INFO)
        else:
            root_log.setLevel(logging.WARN)

    def get_verbosity(self) -> Verbosity:
        """Returns verbosity level for logging."""
        return self.verbosity

    def get_tmp_dir(self) -> TemporaryDirectory:
        """Return a new TemporaryDirectory object."""
        return TemporaryDirectory(dir=self.tmp_dir_root)

    def get_tmp_dir_root(self) -> str:
        """Return the root of all temp dirs."""
        return self.tmp_dir_root

    def set_tmp_dir_root(self, tmp_dir_root: Optional[str] = None):
        """Set root of all temporary directories.

        To set the value, the following rules are used in decreasing priority:

        1) the tmp_dir_root argument if it is not None
        2) an environment variable (TMPDIR, TEMP, or TMP)
        3) a default temporary directory which is
        4) a directory returned by tempfile.TemporaryDirectory()
        """
        # Check the various possibilities in order of priority.
        env_arr = [
            os.environ.get(k) for k in ['TMPDIR', 'TEMP', 'TMP']
            if k in os.environ
        ]

        dir_arr = [tmp_dir_root] + env_arr + [RVConfig.DEFAULT_TMP_DIR_ROOT]
        dir_arr = [d for d in dir_arr if d is not None]
        tmp_dir_root = dir_arr[0]

        try:
            # Try to create directory
            if not os.path.exists(tmp_dir_root):
                os.makedirs(tmp_dir_root, exist_ok=True)
            # Check that it is actually a directory
            if not os.path.isdir(tmp_dir_root):
                raise Exception('{} is not a directory.'.format(tmp_dir_root))
            # Can we interact with directory?
            Path.touch(Path(os.path.join(tmp_dir_root, '.can_touch')))
            # All checks have passed by this point
            self.tmp_dir_root = tmp_dir_root

        # If directory cannot be made and/or cannot be interacted
        # with, fall back to default system location.
        except Exception as e:
            system_tmp_dir = TemporaryDirectory().name
            log.warning(
                'Root temporary directory cannot be used: {}. Using root: {}'.
                format(tmp_dir_root, system_tmp_dir))
            self.tmp_dir_root = system_tmp_dir
        finally:
            os.makedirs(self.tmp_dir_root, exist_ok=True)
            log.debug('Temporary directory root is: {}'.format(
                self.tmp_dir_root))

    def set_everett_config(self,
                           profile: str = None,
                           rv_home: str = None,
                           config_overrides: Dict[str, str] = None):
        """Set Everett config.

        This sets up any other configuration using the Everett library.
        See https://everett.readthedocs.io/

        It roughly mimics the behavior of how the AWS CLI is configured, if that
        is a helpful analogy. Configuration can be specified through configuration
        files, environment variables, and the config_overrides argument in increasing
        order of precedence.

        Configuration files are in the following format:
        ```
        [namespace_1]
        key_11=val_11
        ...
        key_1n=val_1n

        ...

        [namespace_m]
        key_m1=val_m1
        ...
        key_mn=val_mn
        ```

        Each namespace can be used for the configuration of a different plugin.
        Each configuration file is a "profile" with the name of the file being the name
        of the profile. This supports switching between different configuration sets.
        The corresponding environment variable setting for namespace_i and key_ij is
        `namespace_i_key_ij=val_ij`.

        Args:
            profile: name of the RV configuration profile to use. If not set, defaults
                to value of RV_PROFILE env var, or DEFAULT_PROFILE.
            rv_home: a local dir with RV configuration files. If not set, attempts to
                use ~/.rastervision.
            config_overrides: any configuration to override. Each key is of form
                namespace_i_key_ij with corresponding value val_ij.
        """
        if profile is None:
            if os.environ.get('RV_PROFILE'):
                profile = os.environ.get('RV_PROFILE')
            else:
                profile = RVConfig.DEFAULT_PROFILE
        self.profile = profile

        if config_overrides is None:
            config_overrides = {}

        if rv_home is None:
            home = os.path.expanduser('~')
            rv_home = os.path.join(home, '.rastervision')
        self.rv_home = rv_home

        if self.profile == 'local':
            config_ini_env = LocalEnv()
        else:
            config_file_locations = self._discover_config_file_locations(
                self.profile)
            config_ini_env = ConfigIniEnv(config_file_locations)

        self.config = ConfigManager(
            [
                ConfigDictEnv(config_overrides),
                ConfigOSEnv(),
                config_ini_env,
            ],
            doc=(
                'Check https://docs.rastervision.io/ for docs. '
                'Switch to the version being run and search for Raster Vision '
                'Configuration.'))

    def get_namespace_config(self, namespace: str) -> Dict[str, str]:
        """Get the key-val pairs associated with a namespace."""
        return self.config.with_namespace(namespace)

    def get_config_dict(
            self, rv_config_schema: Dict[str, List[str]]) -> Dict[str, str]:
        """Get all Everett configuration.

        This method is used to serialize an Everett configuration so it can be used on
        a remote instance.

        Args:
            rv_config_schema: each key is a namespace; each value is list of keys within
                that namespace

        Returns:
            Each key is of form namespace_i_key_ij with corresponding value val_ij.
        """
        config_dict = {}
        for namespace, keys in rv_config_schema.items():
            for key in keys:
                config_dict[namespace + '_' + key] = \
                    self.get_namespace_config(namespace)(key)
        return config_dict

    def _discover_config_file_locations(self, profile) -> List[str]:
        """Discover the location of RV config files.

        Args:
            profile: the name of the RV profile to use

        Returns:
            a list of paths to RV config files matching the profile name
        """
        result = []

        # Allow for user to specify specific config file
        # in the RV_CONFIG env variable.
        env_specified_path = os.environ.get('RV_CONFIG')
        if env_specified_path:
            result.append(env_specified_path)

        # Allow user to specify config directory that will
        # contain profile configs in RV_CONFIG_DIR
        # env variable. Otherwise, use "$HOME/.rastervision"
        env_specified_dir_path = os.environ.get('RV_CONFIG_DIR')
        if env_specified_dir_path:
            result.append(os.path.join(env_specified_dir_path, profile))
        else:
            result.append(os.path.join(self.rv_home, profile))
        result.append(os.path.join(os.getcwd(), '.rastervision'))

        # Filter out any that do not exist.
        results_that_exist = list(filter(lambda x: os.path.exists(x), result))

        # If the profile is not default, and there is no config that exists,
        # then throw an error.
        if not any(results_that_exist) and profile != RVConfig.DEFAULT_PROFILE:
            raise Exception('Configuration Profile {} not found. '
                            'Checked: {}'.format(profile, ', '.join(result)))

        return results_that_exist
Example #13
0
class RVConfig:
    DEFAULT_PROFILE = 'default'

    tmp_dir = None

    @staticmethod
    def get_tmp_dir():
        if RVConfig.tmp_dir is None:
            RVConfig.set_tmp_dir()
        return tempfile.TemporaryDirectory(dir=RVConfig.tmp_dir)

    @staticmethod
    def get_tmp_dir_root():
        if RVConfig.tmp_dir is None:
            RVConfig.set_tmp_dir()
        return RVConfig.tmp_dir

    @staticmethod
    def set_tmp_dir(tmp_dir=None):
        """Set RVConfig.tmp_dir to well-known value.

        This static method sets the value of RVConfig.tmp_dir to some
        well-known value.  The value is chosen from one of the
        following (in order of preference): an explicit value
        (presumably from the command line) is considered first, then
        values from the environment are considered, then the current
        value of RVConfig.tmp_dir is considered, then a directory from
        tempfile.TemporaryDirectory() is considered.

        Args:
            tmp_dir: Either a string or None.

        """
        DEFAULT_DIR = '/opt/data/tmp/'

        # Check the various possibilities in order of priority.
        tmp_dir_array = [tmp_dir]
        env_array = [
            os.environ.get(k) for k in ['TMPDIR', 'TEMP', 'TMP']
            if k in os.environ
        ]
        current_array = [RVConfig.tmp_dir]
        it = tmp_dir_array + env_array + current_array
        it = list(filter(lambda p: p is not None, it))
        if it:
            explicit_tmp_dir = it[0]
        else:
            explicit_tmp_dir = tempfile.TemporaryDirectory().name

        try:
            # Try to create directory
            if not os.path.exists(explicit_tmp_dir):
                os.makedirs(explicit_tmp_dir, exist_ok=True)
            # Check that it is actually a directory
            if not os.path.isdir(explicit_tmp_dir):
                raise Exception(
                    '{} is not a directory.'.format(explicit_tmp_dir))
            # Can we interact with directory?
            Path.touch(Path(os.path.join(explicit_tmp_dir, '.can_touch')))
            # All checks have passed by this point
            RVConfig.tmp_dir = explicit_tmp_dir

        # If directory cannot be made and/or cannot be interacted
        # with, fall back to default.
        except Exception as e:
            log.warning(
                'Root temporary directory cannot be used: {}. Using root: {}'.
                format(explicit_tmp_dir, DEFAULT_DIR))
            RVConfig.tmp_dir = DEFAULT_DIR
        finally:
            make_dir(RVConfig.tmp_dir)
            log.debug('Temporary directory is: {}'.format(RVConfig.tmp_dir))

    @staticmethod
    def get_instance():
        return rv._registry._get_rv_config()

    def __init__(self,
                 profile=None,
                 rv_home=None,
                 config_overrides=None,
                 tmp_dir=None,
                 verbosity=Verbosity.NORMAL):
        self.verbosity = verbosity

        # Set logging level
        root_log = logging.getLogger('rastervision')
        if self.verbosity >= Verbosity.VERBOSE:
            root_log.setLevel(logging.DEBUG)
        elif self.verbosity >= Verbosity.NORMAL:
            root_log.setLevel(logging.INFO)
        else:
            root_log.setLevel(logging.WARN)

        if tmp_dir is not None:
            self.set_tmp_dir(tmp_dir)

        if profile is None:
            if os.environ.get('RV_PROFILE'):
                profile = os.environ.get('RV_PROFILE')
            else:
                profile = RVConfig.DEFAULT_PROFILE

        if config_overrides is None:
            config_overrides = {}

        if rv_home is None:
            home = os.path.expanduser('~')
            rv_home = os.path.join(home, '.rastervision')
        self.rv_home = rv_home

        config_file_locations = self._discover_config_file_locations(profile)

        help_doc = ('Check https://docs.rastervision.io/ for docs.')
        self.config = ConfigManager(
            # Specify one or more configuration environments in
            # the order they should be checked
            [
                # Allow overrides
                ConfigDictEnv(config_overrides),

                # Looks in OS environment first
                ConfigOSEnv(),

                # Look for an .env file
                ConfigEnvFileEnv('.env'),

                # Looks in INI files in order specified
                ConfigIniEnv(config_file_locations),
            ],

            # Make it easy for users to find your configuration docs
            doc=help_doc)

    def _discover_config_file_locations(self, profile):
        result = []

        # Allow for user to specify specific config file
        # in the RASTERVISION_CONFIG env variable.
        env_specified_path = os.environ.get('RV_CONFIG')
        if env_specified_path:
            result.append(env_specified_path)

        # Allow user to specify config directory that will
        # contain profile configs in RASTERVISION_CONFIG_DIR
        # env variable. Otherwise, use "$HOME/.rastervision"
        env_specified_dir_path = os.environ.get('RV_CONFIG_DIR')
        if env_specified_dir_path:
            result.append(os.path.join(env_specified_dir_path, profile))
        else:
            result.append(os.path.join(self.rv_home, profile))
        result.append(os.path.join(os.getcwd(), '.rastervision'))

        # Filter out any that do not exist.
        results_that_exist = list(filter(lambda x: os.path.exists(x), result))

        # If the profile is not default, and there is no config that exists,
        # then throw an error.
        if not any(results_that_exist) and profile != RVConfig.DEFAULT_PROFILE:
            raise rv.ConfigError('Configuration Profile {} not found. '
                                 'Checked: {}'.format(profile,
                                                      ', '.join(result)))

        return results_that_exist

    def get_subconfig(self, namespace):
        return self.config.with_namespace(namespace)

    def get_model_defaults(self):
        """Return the "model defaults"

        The model defaults is a json file that lists a set of default
        configurations for models, per backend and model key.
        There are defaults that are installed with Raster Vision, but
        users can override these defaults with their own by setting
        the "model_defaults_uri" in the [RV] section of
        thier configuration file, or by setting the RV_MODEL_DEFAULT_URI
        environment variable.
        """
        subconfig = self.get_subconfig('RV')
        default_path = os.path.join(os.path.dirname(rv.backend.__file__),
                                    'model_defaults.json')
        model_default_uri = subconfig('model_defaults_uri',
                                      default=default_path)

        model_defaults = json.loads(file_to_str(model_default_uri))

        return model_defaults

    def get_verbosity(self):
        return self.verbosity
Example #14
0
class RVConfig:
    DEFAULT_PROFILE = 'default'

    tmp_dir = None

    def __init__(self):
        self.reset()

    def reset(self,
              profile=None,
              rv_home=None,
              config_overrides=None,
              tmp_dir=None,
              verbosity=Verbosity.NORMAL):
        self.verbosity = verbosity

        root_log = logging.getLogger('rastervision2')
        if self.verbosity >= Verbosity.VERBOSE:
            root_log.setLevel(logging.DEBUG)
        elif self.verbosity >= Verbosity.NORMAL:
            root_log.setLevel(logging.INFO)
        else:
            root_log.setLevel(logging.WARN)

        if tmp_dir is not None:
            self.set_tmp_dir(tmp_dir)

        if profile is None:
            if os.environ.get('RV_PROFILE'):
                profile = os.environ.get('RV_PROFILE')
            else:
                profile = RVConfig.DEFAULT_PROFILE

        if config_overrides is None:
            config_overrides = {}

        if rv_home is None:
            home = os.path.expanduser('~')
            rv_home = os.path.join(home, '.rastervision')
        self.rv_home = rv_home

        config_file_locations = self._discover_config_file_locations(profile)

        self.config = ConfigManager(
            [
                ConfigDictEnv(config_overrides),
                ConfigOSEnv(),
                ConfigIniEnv(config_file_locations),
            ],
            doc='Check https://docs.rastervision.io/ for docs.')

    def _discover_config_file_locations(self, profile):
        result = []

        # Allow for user to specify specific config file
        # in the RV_CONFIG env variable.
        env_specified_path = os.environ.get('RV_CONFIG')
        if env_specified_path:
            result.append(env_specified_path)

        # Allow user to specify config directory that will
        # contain profile configs in RV_CONFIG_DIR
        # env variable. Otherwise, use "$HOME/.rastervision"
        env_specified_dir_path = os.environ.get('RV_CONFIG_DIR')
        if env_specified_dir_path:
            result.append(os.path.join(env_specified_dir_path, profile))
        else:
            result.append(os.path.join(self.rv_home, profile))
        result.append(os.path.join(os.getcwd(), '.rastervision'))

        # Filter out any that do not exist.
        results_that_exist = list(filter(lambda x: os.path.exists(x), result))

        # If the profile is not default, and there is no config that exists,
        # then throw an error.
        if not any(results_that_exist) and profile != RVConfig.DEFAULT_PROFILE:
            raise Exception('Configuration Profile {} not found. '
                            'Checked: {}'.format(profile, ', '.join(result)))

        return results_that_exist

    @staticmethod
    def get_tmp_dir():
        if RVConfig.tmp_dir is None:
            RVConfig.set_tmp_dir()
        return tempfile.TemporaryDirectory(dir=RVConfig.tmp_dir)

    @staticmethod
    def get_tmp_dir_root():
        if RVConfig.tmp_dir is None:
            RVConfig.set_tmp_dir()
        return RVConfig.tmp_dir

    @staticmethod
    def set_tmp_dir(tmp_dir=None):
        """Set RVConfig.tmp_dir to well-known value.

        This static method sets the value of RVConfig.tmp_dir to some
        well-known value. The value is chosen from one of the
        following (in order of preference): an explicit value
        (presumably from the command line) is considered first, then
        values from the environment are considered, then the current
        value of RVConfig.tmp_dir is considered, then a directory from
        tempfile.TemporaryDirectory() is considered.

        Args:
            tmp_dir: Either a string or None.
        """
        DEFAULT_DIR = '/opt/data/tmp/'

        # Check the various possibilities in order of priority.

        env_arr = [
            os.environ.get(k) for k in ['TMPDIR', 'TEMP', 'TMP']
            if k in os.environ
        ]

        dir_arr = [tmp_dir] + env_arr + [RVConfig.tmp_dir]
        dir_arr = [d for d in dir_arr if d is not None]
        tmp_dir = dir_arr[0] if dir_arr else tempfile.TemporaryDirectory().name

        try:
            # Try to create directory
            if not os.path.exists(tmp_dir):
                os.makedirs(tmp_dir, exist_ok=True)
            # Check that it is actually a directory
            if not os.path.isdir(tmp_dir):
                raise Exception('{} is not a directory.'.format(tmp_dir))
            # Can we interact with directory?
            Path.touch(Path(os.path.join(tmp_dir, '.can_touch')))
            # All checks have passed by this point
            RVConfig.tmp_dir = tmp_dir

        # If directory cannot be made and/or cannot be interacted
        # with, fall back to default.
        except Exception as e:
            log.warning(
                'Root temporary directory cannot be used: {}. Using root: {}'.
                format(tmp_dir, DEFAULT_DIR))
            RVConfig.tmp_dir = DEFAULT_DIR
        finally:
            os.makedirs(RVConfig.tmp_dir, exist_ok=True)
            log.debug('Temporary directory is: {}'.format(RVConfig.tmp_dir))

    def get_subconfig(self, namespace):
        return self.config.with_namespace(namespace)

    def get_verbosity(self):
        return self.verbosity

    def get_config_dict(self, rv_config_schema):
        config_dict = {}
        for namespace, keys in rv_config_schema.items():
            for key in keys:
                config_dict[namespace + '_' + key] = \
                    self.get_subconfig(namespace)(key)
        return config_dict