pyguides

configparser

The configparser module provides the ConfigParser class for reading and writing configuration files in the INI file format. INI files are a simple text format with sections and key-value pairs, commonly used for application configuration. ConfigParser treats configuration files similar to dictionaries, with sections as top-level keys and options within each section. Values are stored as strings but can be automatically converted to integers, floats, or booleans using built-in getter methods.

Syntax

import configparser

# Create a parser
config = configparser.ConfigParser()

# Or with custom options
config = configparser.ConfigParser(
    defaults={'debug': 'true'},
    allow_no_value=True,
    strict=False
)

ConfigParser Classes

configparser.ConfigParser

The main configuration parser class with interpolation support.

ParameterTypeDefaultDescription
defaultsdictNoneDictionary of default values for the DEFAULT section
dict_typetypedictDictionary class to use for sections and options
allow_no_valueboolFalseWhether to accept options without values
delimiterstuple('=', ':')Characters that separate keys from values
comment_prefixestuple('#', ';')Characters that start comments
strictboolTrueWhether to raise on duplicate sections/options
interpolationobjectBasicInterpolation()Value interpolation handler

configparser.RawConfigParser

Legacy variant without interpolation by default, allows non-string values internally.

ConfigParser Methods

read()

Read and parse configuration files.

ParameterTypeDefaultDescription
filenamesstr/listSingle filename or iterable of filenames
encodingstrNoneFile encoding
config.read('config.ini')
config.read(['site.cfg', 'user.cfg'], encoding='utf-8')

read_string()

Parse configuration from a string.

ParameterTypeDefaultDescription
stringstrConfiguration string to parse
sourcestr'<string>'Name for error messages
config.read_string("""
[DEFAULT]
debug = true

[database]
host = localhost
port = 5432
""")

read_dict()

Load configuration from a dictionary.

ParameterTypeDefaultDescription
dictionarydictDictionary with section names as keys
sourcestr'<dict>'Name for error messages
config.read_dict({
    'DEFAULT': {'debug': 'true'},
    'database': {'host': 'localhost', 'port': '5432'}
})

After loading configuration data through any of the reading methods, you need a way to navigate the parsed structure programmatically. ConfigParser provides several introspection methods that let you discover what sections and options are available without hardcoding section names, which is especially important when working with configuration files whose structure may vary across environments or deployment targets.

sections()

Return a list of section names (excluding DEFAULT).

config.sections()
# ['database', 'logging', 'server']

Once you have the list of sections, you can inspect the keys available in any individual section with the options method. This returns a list of all option names defined in that section, excluding the DEFAULT section unless it has been explicitly merged. Knowing the available options is essential when you need to iterate over a section contents or validate that expected settings are present before reading their values.

options(section)

Return list of options in a section.

config.options('database')
# ['host', 'port', 'user', 'password']

After identifying which options exist in a section, the next step is retrieving their actual values. The get method is the primary way to read a string value from a specific section and option pair. It supports optional fallback values when an option might not be present and can control whether interpolation is applied to the retrieved value, giving you fine-grained control over how configuration data flows into your application.

get(section, option, **kwargs)

Get a string value with optional fallback.

ParameterTypeDefaultDescription
sectionstrSection name
optionstrOption name
rawboolFalseDisable interpolation
varsdictNoneAdditional variables for interpolation
fallbackanyNoneValue if option not found
config.get('database', 'host')
config.get('database', 'port', fallback=3306)

While the basic get method always returns string values, configuration files frequently contain numbers and other typed data that should not require manual conversion in application code. ConfigParser provides several convenience methods that handle type conversion automatically, saving you from writing repetitive int and float calls and making your configuration-reading code cleaner and less error-prone.

getint(section, option, **kwargs)

Get an integer value (convenience method).

port = config.getint('database', 'port')
# Returns: 5432 (as int)

When your configuration file stores numeric data, retrieving values as strings and converting them manually would be cumbersome. ConfigParser provides dedicated type-conversion methods that handle parsing automatically. The getfloat method works the same way as getint but converts the stored string to a floating-point number, which is particularly useful for configuration values like tax rates, scaling factors, or coordinate positions where fractional precision matters.

getfloat(section, option, **kwargs)

Get a float value (convenience method).

rate = config.getfloat('rates', 'tax_rate')
# Returns: 0.0825 (as float)

Not all configuration data is numeric. Many settings represent on/off states, feature flags, or yes/no decisions. The getboolean method recognizes a wide range of human-readable boolean representations including yes and no, true and false, on and off, and the digits 1 and 0, all matched case-insensitively. This flexibility means your configuration files can use natural language without forcing users to conform to a single rigid convention.

getboolean(section, option, **kwargs)

Get a boolean value. Recognizes ‘yes’/‘no’, ‘true’/‘false’, ‘on’/‘off’, ‘1’/‘0’.

debug = config.getboolean('DEFAULT', 'debug')
# Returns: True (case-insensitive)

While reading configuration is the most common operation, applications often need to modify settings programmatically and persist those changes back to disk. The set method accepts a section name, an option name, and a string value, storing it in memory. Note that both the option name and value must be strings. If you need to store other types, you must convert them to strings before calling set and use the corresponding getter methods when reading them back.

set(section, option, value)

Set an option value. Both option and value must be strings.

config.set('database', 'host', 'db.example.com')
config.set('database', 'port', '5432')

After making changes to a ConfigParser object in memory, you can persist those modifications to a file with the write method. It takes an open file object in text mode and an optional parameter that controls whether spaces appear around the equals sign delimiter. Writing with spaces around delimiters produces more readable output, which is the default behavior and recommended for configuration files that humans may need to inspect or edit directly.

write(fileobject, space_around_delimiters=True)

Write configuration to a file.

ParameterTypeDefaultDescription
fileobjectfileOpen file object in text mode
space_around_delimitersboolTrueAdd spaces around = and :
with open('config.ini', 'w') as f:
    config.write(f)

Beyond reading and writing existing structures, ConfigParser supports programmatic creation of new configuration sections. The add_section method creates a new named section in the parser, raising a DuplicateSectionError if a section with that name already exists when strict mode is enabled. This guard helps catch accidental overwrites early, which can save debugging time when configuration files grow large across multiple contributors.

add_section(section)

Add a new section. Raises DuplicateSectionError if it exists.

config.add_section('new_section')

Before attempting to add or access a section, it is often prudent to check whether it already exists. The has_section method returns a boolean indicating whether a section with the given name is present in the parser, including the DEFAULT section. This check is especially valuable in conditional configuration loading scenarios where different sections may be required depending on the execution environment or application mode.

has_section(section)

Check if a section exists.

if config.has_section('database'):
    # process database config

Once you have confirmed that a section exists, you may still need to verify the presence of individual options within it. The has_option method checks whether a specific key is defined in a given section, returning a simple boolean. This method is commonly used before calling get with a fallback value or when displaying configuration summaries that highlight which settings have been explicitly configured versus relying on defaults.

has_option(section, option)

Check if an option exists in a section.

config.has_option('database', 'host')
# Returns: True or False

ConfigParser also supports removing individual settings from a section using remove_option. This is useful when merging configuration sources or when an application needs to strip sensitive values like passwords from an in-memory configuration before passing it to a logging subsystem or external service. The method returns True if the option existed and was removed, or False if it was not present.

remove_option(section, option)

Remove an option from a section.

config.remove_option('database', 'password')

For larger-scale configuration cleanup, the remove_section method deletes an entire section and all of its options at once. This is particularly helpful when reloading configuration from scratch or when deprecating a feature that had its own configuration section. After removal, any subsequent attempt to access that section will raise a NoSectionError, so it is wise to guard subsequent access with has_section checks.

remove_section(section)

Remove a section entirely.

config.remove_section('testing')

Now that we have covered the individual methods, it is helpful to see how they combine in practice. The following examples demonstrate common workflows starting with the most fundamental task, which is creating a ConfigParser object, populating it with default and application-specific settings organized into sections, and then writing the complete configuration out to a file for persistent storage.

Examples

Creating and writing a config file

import configparser

config = configparser.ConfigParser()
config['DEFAULT'] = {
    'debug': 'true',
    'log_level': 'INFO'
}
config['database'] = {
    'host': 'localhost',
    'port': '5432',
    'name': 'myapp'
}
config['server'] = {
    'host': '0.0.0.0',
    'port': '8080'
}

with open('config.ini', 'w') as f:
    config.write(f)

# Output:
# [DEFAULT]
# debug = true
# log_level = INFO
#
# [database]
# host = localhost
# name = myapp
# port = 5432
#
# [server]
# host = 0.0.0.0
# port = 8080

After the configuration file has been written, the next logical step is reading it back and extracting values in a typed manner. ConfigParser supports both dictionary-style access using square bracket notation and method-based access with automatic type conversion. The following example reads the file we just created, demonstrates both access patterns, and shows how to iterate over all sections and their key-value pairs, which is a common pattern when generating configuration reports or validating completeness.

Reading and accessing config values

import configparser

config = configparser.ConfigParser()
config.read('config.ini')

# Access via mapping protocol
host = config['database']['host']
port = config['database']['port']

# Access via methods with type conversion
port_int = config.getint('database', 'port')
is_debug = config.getboolean('DEFAULT', 'debug')

# Iterate over sections and options
for section in config.sections():
    print(f'[{section}]')
    for key, value in config[section].items():
        print(f'  {key} = {value}')

One of the most powerful features of ConfigParser is value interpolation, which allows configuration values to reference other values using a simple percent-sign syntax. This eliminates duplication in configuration files and makes it possible to define base paths, prefixes, or common settings once and reuse them across multiple options. The following example sets up a paths section where the data and logs directories are derived from a single home directory value, demonstrating how interpolation chains can produce clean and maintainable configuration structures.

Using interpolation

Interpolation lets values reference other values using %(key)s syntax:

config = configparser.ConfigParser()

config['paths'] = {
    'home': '/home/user',
    'data': '%(home)s/data',
    'logs': '%(data)s/logs'
}

print(config.get('paths', 'data'))
# Output: /home/user/data

print(config.get('paths', 'logs'))
# Output: /home/user/data/logs

# Disable interpolation
config_no_interp = configparser.ConfigParser(interpolation=None)
config_no_interp.read_dict({'paths': {'data': '%(home)s/data'}})
print(config_no_interp.get('paths', 'data'))
# Output: %(home)s/data

In real-world configuration files, not every option will always be present. Users may have partial configuration, or optional settings may be omitted intentionally. Rather than surrounding every get call with try and except blocks, ConfigParser accepts a fallback keyword argument that provides a default value when the requested option is not found. The following example shows how fallbacks work with string, integer, and boolean getter methods, including the pattern of using None as a sentinel to detect genuinely unset options.

Handling missing values with fallbacks

config = configparser.ConfigParser()
config.read_dict({
    'database': {'host': 'localhost', 'port': '5432'}
})

# Fallback when option doesn't exist
timeout = config.get('database', 'timeout', fallback=30)

# Fallback with getboolean
debug = config.getboolean('database', 'debug', fallback=False)

# Using None as fallback
password = config.get('database', 'password', fallback=None)
if password is None:
    print('No password configured')

ConfigParser really shines when you need to layer configuration from multiple sources. A typical pattern reads a system-wide defaults file first, then a site-specific or user-specific file that overrides selected values. Because read accepts a list of filenames and processes them in order, later files automatically take precedence for any keys they define. This layered approach lets you ship sensible defaults while giving deployers and users the ability to customize behavior without editing the original configuration files.

Common Patterns

Loading config from multiple sources with priority

Later files override earlier ones for conflicting keys:

config = configparser.ConfigParser()
config.read(['defaults.cfg', 'site.cfg', 'local.cfg'])
# local.cfg has highest priority

While the built-in boolean recognition covers the most common truthy and falsy strings, some applications use domain-specific terminology that does not match the defaults. ConfigParser exposes the BOOLEAN_STATES dictionary, which you can replace or extend with your own mappings. This is particularly useful when integrating with legacy configuration formats or when your application uses words like enabled and disabled that ConfigParser does not recognize by default.

Customizing boolean values

config = configparser.ConfigParser()
config.BOOLEAN_STATES = {'enabled': True, 'disabled': False, 'on': True, 'off': False}
config['app']['feature'] = 'enabled'
config.getboolean('app', 'feature')  # Returns: True

By default, ConfigParser converts all option names to lowercase when reading configuration files, which makes lookups case-insensitive and is almost always what you want. However, some use cases require preserving the original casing, such as when option names map directly to environment variables or external system identifiers. Setting the optionxform attribute to the built-in str function disables the lowercase transformation entirely, allowing option names to retain their original case as they appear in the source file.

Preserving case in option names

config = configparser.ConfigParser()
config.optionxform = str  # Disable lowercase conversion
config.read_string("""
[Section]
OptionName = value
""")
print(list(config['Section'].keys()))
# Output: ['OptionName'] (not ['optionname'])

Not all configuration entries need explicit values, particularly when the mere presence of a key signals a feature flag or setting. By creating a ConfigParser with allow_no_value set to True, you can define options that consist of a key alone, which will return None when accessed. This pattern is common in tools like SSH client configuration files where entries such as ForwardAgent or Compression act as boolean toggles without requiring an equals sign and a value.

Working with options without values

config = configparser.ConfigParser(allow_no_value=True)
config.read_string("""
[settings]
debug
verbose = true
skip_validation
""")

print(config['settings']['debug'])
# Output: None

print(config['settings']['verbose'])
# Output: true

Errors

ExceptionDescription
NoSectionErrorRequested section does not exist
NoOptionErrorRequested option does not exist
DuplicateSectionErrorSection already exists (in strict mode)
DuplicateOptionErrorOption already exists in section (strict mode)
InterpolationErrorError during value interpolation
ParsingErrorError parsing the configuration file
try:
    host = config.get('database', 'host')
except configparser.NoSectionError:
    print('Database section not found')
except configparser.NoOptionError:
    print('Host option not defined')

See Also

  • argparse — command-line argument parsing
  • json — JSON configuration files
  • pathlib — filesystem path handling
  • tomllib — TOML file parsing (built-in since Python 3.11)