Skip to content

etingof/scopedconfig

Repository files navigation

Hierarchical configuration for Python packages

PyPI Python Versions Build status Coverage Status GitHub license

The scopedconfig library offers a simple to use and lightweight configuration file parser for Python packages that desire their configuration information to be expressed in a hierarchical manner.

How to use scopedconfig

With scopedconfig you turn a text file containing hierarchical configuration, expressed in a scoped option-value form, into an object through which you can look up values by option names.

The configuration file is composed of a set of option: value pairs optionally enclosed into potentially nested block {} constructs. Blocks provide lexical scopes for options (so you can have same-named options in different scopes) and a way to override the value defined in the outer scope by the value in the inner scope.

Options are distinguished from values by trailing colon (:). There may be several whitespace-separated values assigned to option. Values with spaces can be quoted.

Much like directory names on the file system, block names form a path to the nested scopes to look up options at starting from the innermost.

For example:

test-option: global-default-value

outermost-block
{
    test-option: a-bit-more-specific-value

    more-specific-block
    {
        test-option: specific-value

        very-concrete-settings
        {
            test-option: highly-specific-value
        }
    }
}

Evaluating the above configuration for test-option would yield:

get_option('test-option', '') # -> global-default-value
get_option('test-option', 'outermost-block') # -> a-bit-more-specific-value
get_option('test-option', 'outermost-block',
           'more-specific-block') # -> specific-value
get_option('test-option', 'outermost-block', 'more-specific-block',
           'very-concrete-settings')  # -> highly-specific-value

Options specified inside a block apply to their current scopes as well as to all nested scopes unless the same option exists there:

$ cat config.txt
outermost-block
{
    test-option: test-value

    more-specific-block
    {
        unrelated-option: value
    }
}

Looking up test-option at the above configuration would yield:

get_option('test-option', '') # -> ScopedConfigError raised
get_option('test-option', 'outermost-block') # -> test-value
get_option('test-option', 'outermost-block',
           'more-specific-block') # -> test-value

In Python code, using the above configuration file in an application would look like this:

from scopedconfig import *

with open('config.txt') as iterable:
    cfg = ScopedConfig.load(iterable)

scopes = cfg.get_scopes('test-option')
value = cfg.get_option('test-option', *scopes)
print('Option at scope %s has value %s' % ('.'.join(scopes), value))

In the application, the intended configuration design is to have most general options defined somewhere within the outer scope, more specific values can be defined in the inner scope(s). Application then can look up the option always at the innermost scope catching more specific values for any given option (if it's present), and falling back to a less specific value at the first outward scope where the desired option is present.

How to get scopedconfig

The scopedconfig package is distributed under terms and conditions of 2-clause BSD license. Source code is freely available as a GitHub repo.

You could pip install scopedconfig or download it from PyPI.

If something does not work as expected, open an issue at GitHub.

Copyright (c) 2019, Ilya Etingof. All rights reserved.

About

Hierarchical configuration for Python packages

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages