= 5.1 The configparser module == Introduction to the configparser module Currently, many popular services provide an API that we can use in our applications. Integration with these services requires authentication using data such as a login and password, or simply an access token. Each service may require different data for authentication, but one thing is certain – we need to store it somewhere in our application. It's not a good idea to hardcode them directly in the code. A better solution is to use the configuration file, which will be read by the code. In Python, this is possible thanks to a module called ''configparser''. The ''configparser'' module is available in the Python standard library. To start using it, we need to import the appropriate module: import configparser However, before we start reading the configuration data, we must familiarize ourselves with the structure of the file in which they're stored. Are you curious how this is done? == What does the configuration file look like? The structure of the configuration file is very similar to Microsoft Windows INI files. It consists of sections that are identified by names enclosed in square brackets. The sections contain items consisting of key value pairs. Each pair is separated by a colon '':'' or equals sign ''=''. What's more, the configuration file may contain comments followed by a semicolon '';'' or hash ''#'': [DEFAULT] host = localhost # This is a comment. [mariadb] name = hello user = user password = password [redis] port = 6379 db = 0 Our configuration file contains the ''DEFAULT'', ''mariadb'' and ''redis'' sections. The ''DEFAULT'' section is slightly different because it contains the default values that can be read in the other sections of the file. In our case, there's a common host for all sections. The second section called ''mariadb'' stores the data necessary to connect to the MariaDB database. These are the database name, username, and password. The last section contains ''Redis'' configuration data, consisting of the port and database number. In addition, both in this section and in the ''mariadb'' section, we have access to the host option defined in the ''DEFAULT'' section. In a moment, you'll learn how to read this data using the ''configparser'' module. **NOTE**: The whitespace at the beginning and end of keys and values is removed. == Parsing the configuration file Parsing the configuration file is extremely simple. First, we need to create a ''ConfigParser'' object, which provides many useful methods for parsing data. One of them is the ''read'' method, responsible for reading and parsing the configuration file. In our example, we pass the ''config.ini'' filename to it, but it's also possible to pass a list containing several files. If all goes well, the ''read'' method returns a list of filenames that have been successfully parsed. Let's look at the code in the editor to see how to parse the data stored in the ''config.ini'' file. import configparser config = configparser.ConfigParser() print(config.read('config.ini')) print('Sections:', config.sections(),'\n') print('mariadb section:') print('Host:', config['mariadb']['host']) print('Database:', config['mariadb']['name']) print('Username:', config['mariadb']['user']) print('Password:', config['mariadb']['password'], '\n') print('redis section:') print('Host:', config['redis']['host']) print('Port:', int(config['redis']['port'])) print('Database number:', int(config['redis']['db'])) Result: Sections: ['mariadb', 'redis'] mariadb section: Host: localhost Database: hello Username: user Password: password redis section: Host: localhost Port: 6379 Database number: 0 In the example, we use the ''sections'' method to display the names of sections in the file. Note that the ''DEFAULT'' section doesn't appear in the list of returned sections. This is the default behavior of the ''sections'' method. Access to the data contained in the configuration file is analogous to how we use dictionaries. To obtain any value, use the appropriate key sequence. It's important to note that the section names are case sensitive, while the keys aren't. Despite the fact that the ''DEFAULT'' section is omitted as a result of the ''sections'' method, we still have access to its options. Both the ''mariadb'' and ''redis'' sections can read the ''host'' option. Its also possible to access the values stored in the options by using the ''get'' method. The ''get'' method requires the section name and key to be passed. This is what it looks like in practice: print('Host:', config.get('mariadb', 'host')) # print('Host:', config['mariadb']['host'] == Reading configuration from other sources The ''configparser'' module enables configurations from various sources to be read. One of them is a dictionary that we can load using the ''read_dict''. Look at the code in the editor. import configparser config = configparser.ConfigParser() dict = { 'DEFAULT': { 'host': 'localhost' }, 'mariadb': { 'name': 'hello', 'user': 'root', 'password': 'password' }, 'redis': { 'port': 6379, 'db': 0 } } config.read_dict(dict) print('Sections:', config.sections(),'\n') print('mariadb section:') print('Host:', config['mariadb']['host']) print('Database:', config['mariadb']['name']) print('Username:', config['mariadb']['user']) print('Password:', config['mariadb']['password'], '\n') print('redis section:') print('Host:', config['redis']['host']) print('Port:', int(config['redis']['port'])) print('Database number:', int(config['redis']['db'])) Result: Sections: ['mariadb', 'redis'] mariadb section: Host: localhost Database: hello Username: root Password: password redis section: Host: localhost Port: 6379 Database number: 0 The ''read_dict'' method accepts any dictionary whose keys are section names, while the values include dictionaries containing keys and values. All values read from the dictionary are converted to strings. **NOTE**: The ''configparse''r module also has ''read_file'' and ''read_string'' methods that allow you to read the configuration from an open file or string. You can find more information about these methods in the documentation. == Creating a configuration file Creating a configuration file is as easy as parsing it. If you know how to work with dictionaries, it's a piece of cake. Let's look at a simple example of how to create a configuration file that you already know. Take a look at the code in the editor. import configparser config = configparser.ConfigParser() config['DEFAULT'] = {'host': 'localhost'} config['mariadb'] = {'name': 'hello', 'user': 'root', 'password': 'password'} config['redis'] = {'port': 6379, 'db': 0} with open('config.ini', 'w') as configfile: config.write(configfile) To create a configuration file, you should treat the ''ConfigParser'' object as a dictionary. Note that the section names are keys, while their options are listed in separate dictionaries. The above configuration is saved using the ''write'' method, which requires an open file to be passed in text mode. For this purpose, the built-in open method is used. A configuration loaded using the read method can also be modified. To change a single option, simply set the new value to the appropriate key, and then save the file using the write method: import configparser config = configparser.ConfigParser() config.read('config.ini') config['redis']['db'] = '1' with open('config.ini', 'w') as configfile: config.write(configfile) == Interpolating values The big advantage of the configuration file is the possibility of using interpolation. It allows you to create expressions consisting of a placeholder under which the appropriate value will be substituted. Take a look at the configuration file below: [DEFAULT] host = localhost [mariadb] name = hello user = user password = password [redis] port = 6379 db = 0 dsn = redis://%(host)s The configuration file has been extended with another option called ''dsn''. Its value contains the placeholder ''%(host)s'', which needs to be replaced by an appropriate value. Placing any key between ''%'' and ''s'' informs the parser of the need to interpolate. Of course, all the work is done for us, and we only get the ready results. For the ''dsn'' option, it'll be the following string: ''redis://localhost''. Note that the placeholder ''%(host)s'' has been replaced by the value stored in the host option.