Trying to check if ini file exists, then if so check if a set of keys exist, then write those keys into a dataclass

I am trying to

  1. Check if ini file exists - if it does;
  2. Check if it contains specific keys - if it does;
  3. Add those values into a dataclass

If the file exists, but all keys do not exist - I want there to be an error telling me which keys do not exist.

This refers to the check_ini function;

This is what I have so far, but because I used a for loop above my try - I am unsure how to make number 3 happen. What I would like to happen is - check the six keys exist, add the values into my dataclass, and return that datacalss. Here is what I have so far. Any thoughts on how to do the try piece? Any thoughts / direction appreciated.

    import os
    from os.path import exists
    from configparser import ConfigParser
    from configparser import NoOptionError
    from dataclasses import dataclass
    from typing import Optional
    from mypackage import config

    @dataclass
    class config_data:
        success: bool
        source_path: Optional[str] = None

    def read_ini():
        default_path = os.path.expanduser(config.CONFIG_INI_PATH)
        final_path = os.environ.get('INI_PATH', default_path)
        parser = ConfigParser()
        parser.read(final_path)
        return parser

    def check_ini():
        default_path = os.path.expanduser(config.CONFIG_INI_PATH)
        final_path = os.environ.get('INI_PATH', default_path)
        parser = ConfigParser()
        parser.read(final_path)
        ini = read_ini()
        bad_keys = []
        if exists(final_path):
            keys = ("app", "source_path", "good", "conf_path", "rad", "suffix")
            for option in keys:
                try:
                    ini.get("default", option)
                    #config_data('True', 'source_oath')
                except NoOptionError:
                    #print(NoOptionError(option, "default"))
                    print(f'This key is missing: {option}')

Hello, @swills1. I hope the code below will help you.

import os
from os.path import exists
from configparser import ConfigParser,NoOptionError
from dataclasses import dataclass
from typing import Optional
from mypackage import config

@dataclass
class config_data:
    success: bool
    data: dict
    source_path: Optional[str] = None

def read_ini():
    default_path = os.path.expanduser(config.CONFIG_INI_PATH)
    final_path = os.environ.get('INI_PATH', default_path)
    parser = ConfigParser()
    parser.read(final_path)
    return (parser,final_path)

def check_ini():
    iniKeysAndValues = {}
    iniAndFinalPath = read_ini()
    ini = iniAndFinalPath[0]
    final_path = iniAndFinalPath[1]
    if exists(final_path):
        keys = ("app", "source_path", "good", "conf_path", "rad", "suffix")
        for option in keys:
            try:
                keyValue = ini.get("default", option)
                iniKeysAndValues.setdefault(option,keyValue)
            except NoOptionError:
                iniKeysAndValues.setdefault(option,None)#If you like, you can remove this line. Removing may be more sensible in some case.
                print(f'This key is missing: {option}')
        return config_data(True,iniKeysAndValues,final_path)

print(check_ini())#Check if it works

Note: This code uses a dictionary to collect data.

Thank you, @soil. This is brilliant! One final question, since the return always returns even if the except is triggered, would it make sense to add a finally so that if the except was triggered, it fails / gives the error instead of returning the data? Or is there a better way? Thank you.

The source_path is a value in the ini file so the data dict encompasses it. I didn’t think to use a dict in the dataclass. That makes everything a lot easier. Thank you very much.

You are welcome. You can remove this line. This way, it won’t add a new key to the dictionary if except is triggered.(I hope I understood your question well)

iniKeysAndValues.setdefault(option,None)#If you like, you can remove this line. Removing may be more sensible in some case.

Thank you. To clarify, right now - if there is a key in keys that isn’t in the ini file, but the other keys are - config_data() is still returned.

Say keys looked like;

keys = ("app", "source_path", "kanga")

I would get the following result because it found app and source_path in the ini file.

This key is missing: kanga
config_data(success=True, data={'app': 'app', 'source_path': 'source'})

I am asking how to basically error out if there is a key in keys that is not in the ini file rather than show config_data() no matter what.

Thanks again,

But raising a real error will crash your program. If you are still sure you want to raise, you can remove the except block and its inner code. You can also raise an exception manually, just updating your for-loop code like that:

if option in ini["default"]:#checks if "option" key exists in "default" section
    keyValue = ini.get("default", option)
    iniKeysAndValues.setdefault(option,keyValue)
else: 
    raise Exception(f"We couldn't find this key in your file:{option}")

Thanks. Yes, it needs to break if the keys aren’t found. The keys are needed for the program, so if they aren’t there - it will break anyway. Thanks again!

You are welcome. I’m glad to help you.

1 Like

I just realized the genius of this.

iniKeysAndValues.setdefault(option,None)=

Just wanted to let you know it finally hit me what you were thinking. You saw the future. Thanks again.

But, that line may create an “error”. If your ini file has a key which hasn’t got a value, {key_name:None} may confuse everything. So, make sure that you haven’t got any empty keys(they generally has this syntax: keyName= ) in your ini file before using this code line.(Using empty variables can also cause a kind of real parserError. You can see this documentation about it.)

I had changed it to an empty string instead of none and tested that scenario. Works flawlessly.

iniKeysAndValues.setdefault(option,'')#If you like, you can remove this line. Removing may be more sensible in some case.

I’ll read that linked documentation. Thanks.

1 Like

Okay. Good luck in your project!(If you still have questions or something to say, you can just write it)
Note:You still should be careful about that line. :grin:

1 Like