pythonconfigparser

Why does this function work on macOS and not on Windows?


I have a function that is reading a file with configparser and on Windows I have problems with the file encoding.

This is the function:

def getSetting(name,section):
    cpass = configparser.RawConfigParser()
    cpass.read('data/settings.data', encoding='UTF-8')
    

    value = 'False'

    for each_section in cpass.sections():
        for (each_key, each_val) in cpass.items(each_section):
            if each_section == section and each_key == name:
                value = each_val

    if name == 'log' and value == 'False':
        value = translations['disabilitato_first_cap']
        
    return value

I've tried with this, as told in a question I found on Stack Overflow itself:

def getSetting(name,section):
    cpass = configparser.RawConfigParser()

    with open('data/settings.data', 'r', encoding='utf-8') as f:
        cpass.read_file(f)

        value = 'False'

        for each_section in cpass.sections():
            for (each_key, each_val) in cpass.items(each_section):
                if each_section == section and each_key == name:
                    value = each_val

        if name == 'log' and value == 'False':
            value = translations['disabilitato_first_cap']
            
    return value

And I've tried also adding this line on top of my Python files:

# -*- coding: utf-8 -*-

But nothing really works.

This is the function that generate the file:

def checkSettings(if_false_create):
    cpass = configparser.RawConfigParser()
    try:
        with open('data/settings.data', encoding="UTF-8") as f:
            cpass.read_file(f)
            return True
    except IOError:
        print(colors.wy+" "+translations['impostazioni_base_configurate'])
        if if_false_create == False:
            return False
        else:
            log = translations['disabilitato_first_cap']
            settings = configparser.RawConfigParser()
            settings.add_section("general_settings")
            settings.set("general_settings", "log", translations['disabilitato_first_cap'])
            settings.set("general_settings", "analyze_account", "doublegram_test_user")
            settings.set("general_settings", "between_autoinvite_pause", "1-3 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            settings.set("general_settings", "between_invite_pause", "1-3 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            setup = open("data/settings.data", "w")
            settings.write(setup)

            settings.add_section("adding_settings")
            settings.set("adding_settings", "between_autoinvite_pause", "1-3 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            settings.set("adding_settings", "auto_add_at_start", translations['abilitato_first_cap'])
            settings.set("adding_settings", "if_account_out", translations['abilitato_first_cap'])
            settings.set("adding_settings", "change_account_n_requests", "20 "+translations['richieste'])
            settings.set("adding_settings", "change_account_pause", "1-5 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            settings.set("adding_settings", "consecutive_error_breaker", "30 "+translations['errori'])
            settings.set("adding_settings", "start_point_members_file", translations['da_interrotto'])
            settings.set("adding_settings", "add_using", translations['user_id_opt'])
            settings.set("adding_settings", "between_adding_pause", "1-3 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            settings.set("adding_settings", "casual_pause_times", "3-120 "+translations['abbreviazione_secondi']+" ("+translations['casuale']+")")
            settings.set("adding_settings", "exclude_bot", translations['abilitato_first_cap'])
            settings.set("adding_settings", "exclude_admin", translations['abilitato_first_cap'])
            settings.set("adding_settings", "photo_forcing", translations['abilitato_first_cap'])
            settings.set("adding_settings", "filter_last_seen", translations['nessuna_restrizione'])
            settings.set("adding_settings", "filter_dc", translations['nessuna_restrizione'])
            settings.set("adding_settings", "filter_phone_number", translations['disabilitato_first_cap'])
            settings.set("adding_settings", "stop_max_adding", translations['nessun_limite'])
            settings.set("adding_settings", "continuous_adding", translations['disabilitato_first_cap'])
            settings.set("adding_settings", "ca_ripet", translations['infinito'])
            settings.set("adding_settings", "ca_pause", translations['nessuna_pausa'])
            setup = open("data/settings.data", "w")
            settings.write(setup)
            
            setup.close()
            return log

Solution

  • Depending on the platform you're using, python behaves differently in relation to the encoding of string/binary data. In Linux, your locale will typically define what encoding the data will be processed with. When you're at a logged on desktop, you may end up with UTF-8 default encoding, but when you run under an environment such as cron, or in docker, you end up with ascii encoding. On the Mac, it's almost always UTF-8. On Windows, you typically end up with one of it's codepages, such as cp1252.

    The long and short of this is that every time you read or write data you need to specify the encoding to the open function, to ensure that we process the file appropriately. If we forget the encoding at one side, then we can be stung on the other side.

    It's even got PEP-597, to help people by introducing a warning when you forget to use an encoding. If you pass your code throught pylint and pyflakes, it will also remind you that you should use the encoding parameter.

    So every:

    open(filename)
    

    that isn't binary, should almost always have an explicit encoding= parameter. Forgetting the parameter is almost always a mistake.