Generating Data From INI Configuration Files in Python
|Python provides a number of methods to generate data. These range from for
loop, list comprehension, and range
to NumPy sequences such as arange
, linspace
, logspace
, and geomspace
. Each of these have specific input parameters but they differ in the output. In this article, I’ll discuss a method to generate data under a single function with parameters read from an INI configuration file.
Using ConfigParser
to Read from INI Files
The library to read from configuration files is ConfigParser. The usage is quite simple. We simply instantiate an object and then provide the filename to the read
method.
import from configparser import ConfigParser
config = ConfigParser()
config.read(filename)
Once it’s provided, we can access keys within a section of the INI file using the get
method.
value = config.get(section, key)
The plain get
method will return strings. If the values are numbers, getint
will return an integer and getfloat
will return a float.
Defining Data Generation Mode and Data Type
The two modes we’ll cover are discrete and step. These two modes directly translate to a simple discrete sequence of values and also a sequence of step values. We can use the internal range
function for step values but this doesn’t allow for floating point numbers. An alternative is to use np.arange()
but if you need to loop over the points rather than have them in memory, it’s better to create a generator function.
The method I’ve selected is to define a frange()
function to generate a range of floating-point numbers in both positive and negative directions.
For a more thorough explanation of the differences between range
and np.arange
visit https://realpython.com/how-to-use-numpy-arange/#comparison-of-range-and-nparange.
def frange(start, stop=None, step=None):
"""This provides a floating point range function is both positive and negative directions"""
if stop is None:
stop = start
start = 0
if step is None:
step = 1
i = start
while True:
if step > 0 and i >= stop:
break
elif step < 0 and i <= stop:
break
yield i
i = round(i + step, 14) # round off floating point error
In addition to the mode, we’ll need to define the data type to ensure that the proper data type is returned. We use a dtype key for this and allow types int, float, and string.
mode = config.get(section, "mode", fallback="discrete")
dtype = config.get(section, "dtype", fallback="string")
We use the fallback parameters here to default to discrete strings. You could choose somethings different. With discrete strings as default, the letters section of the INI doesn’t need either mode or dtype keys.
If the mode is discrete, we’ll need to read the values and convert to the required data type. Since the individual values are provided in a comma separated format, we’ll need to split the string and the convert.
if mode == "discrete":
if dtype == "int":
values = map(int, config.get(name, "values").split(","))
elif dtype == "float":
values = map(float, config.get(name, "values").split(","))
else:
values = map(str, config.get(name, "values").split(","))
If the mode is step, we’ll need to read the start
, stop
, and step
values, convert to required data type, and then call frange
().
elif mode == "step":
if dtype == "int":
start = config.getint(name, "start")
stop = config.getint(name, "stop")
step = config.getint(name, "step")
elif dtype == "float":
start = config.getfloat(name, "start")
stop = config.getfloat(name, "stop")
step = config.getfloat(name, "step")
stop = round(stop + step, 14) # increment stop for inclusive range and round off floating point error
values = frange(start, stop, step)
Finally, we return the values either as a list or generator object.
return values