A configuration file is a text file which specifies values for some pre-defined keys. For example, the following configuration specifies a few values (some of which depend on other values) and has a comment that is ignored:

# example of a simple configuration file
data_dir: ~/data
terra_dir: %(data_dir)s/MODIS/Terra/C5
aqua_dir: %(data_dir)s/MODIS/Aqua/C5

This config file can be read with:

IDL> config = mg_read_config('simple.ini')
IDL> print, config->get('terra_dir')
~/data/MODIS/Terra/C5

My configuration file reader supports sections, interpolation (substituting a variable into another one, as above), and default values.

The returned value for my reader is an object with three methods: has_option, get, and put, but also supports overloaded operators for brackets, PRINT, and HELP.

Let’s consider a more complicated configuration file, sections.ini:

# example of using sections to group items (notice the same name can be used in
# different sections without conflict)

# these are options in the default section that can be accessed from any other
# section
data_root: ~/data
output_root: %(data_dir)s/processed

# Terra specific options
[ Terra ]
subdir: MODIS/Terra/C5
data: %(data_root)s/%(subdir)s
output: %(output_root)s/%(subdir)s
variables: [ Longitude, Latitude, Optical_Depth_Land_And_Ocean ]
grid: %(data_dir)s/grids/NCEP-T62.nc

# Aqua specific options
[ Aqua ]
subdir: MODIS/Aqua/C5
data: %(data_root)s/%(subdir)s
output: %(output_root)s/%(subdir)s
variables: [ Longitude, Latitude, Deep_Blue_Angstrom_Exponent_Land ]
grid: %(data_dir)s/grids/MERRA-125x1.25.nc

Read this file in the same manner as the simpler one:

IDL> config = mg_read_config('sections.ini')

Use the has_option and get methods to determine if particular options are present and to retrieve their values:

IDL> print, config->has_option('data_root')
1
IDL> print, config->get('data_root')
~/data

To retrieve an option that is not in the default section, the SECTION keyword must be used:

IDL> print, config->get('data', section='Terra')
~/data/MODIS/Terra/C5
IDL> print, config->get('variables', section='Terra')
[ Longitude, Latitude, Optical_Depth_Land_And_Ocean ]

Arrays can be extracted during the get method, if that is more useful:

IDL> terra_vars = config->get('variables', section='Terra', /extract)
IDL> help, terra_vars
TERRA_VARS STRING = Array[3]
IDL> print, terra_vars
Longitude Latitude Optical_Depth_Land_And_Ocean

The HELP and PRINT routines are overloaded for the config object returned:

IDL> help, config
CONFIG MGFFOPTIONS <NSECTIONS=3 NOPTIONS=12>
IDL> print, config
[ ]
data_root: ~/data
output_root: %(data_dir)s/processed

[ Aqua ]
variables: [ Longitude, Latitude, Deep_Blue_Angstrom_Exponent_Land ]
grid: %(data_dir)s/grids/MERRA-125x1.25.nc
data: %(data_root)s/%(subdir)s
output: %(output_root)s/%(subdir)s
subdir: MODIS/Aqua/C5

[ Terra ]
variables: [ Longitude, Latitude, Optical_Depth_Land_And_Ocean ]
grid: %(data_dir)s/grids/NCEP-T62.nc
data: %(data_root)s/%(subdir)s
output: %(output_root)s/%(subdir)s
subdir: MODIS/Terra/C5

The code is GitHub in my library.