GetSettings in Python

G’day everyone,

Fair cop, I’m trying to translate/pilfer Dale Fugier’s steel section script from rhinoscript to rhinopython.

But GetSettings is giving me grief.

import rhinoscriptsyntax as rs
import scriptcontext
import Rhino

#**************************
# Draws Flat Bar sections
#**************************
def FlatBars():
    
    cmdname="Flat Bar"
    configfile="AllySections.ini"
    section="FLAT_BAR"
    
    filename=rs.FindFile(configfile)
    if not filename:
        msg=("Unable to locate " + configfile + ".")
        rs.MessageBox(msg, 16, cmdname)
        return
    
    
    entries=rs.GetSettings(filename, section)
    if not entries: return

entries=rs.GetSettings(filename,section) keeps giving the errors
"No section: ‘flat_bar’ "

Yet the ini file has this on the first line…
[FLAT_BAR]
;,
50x6 = 50.0,6.0

The help file sugggests there should be no issues with case sensitivity, but I have them as all caps anyway.

In SaveSettings there’s talk of a windows cache keeping the most recently accessed file around. Could this be the problem? If I delete the ini file it cannot be found, so don’t think this is the problem.

Any other thoughts?
cheers,
Nick

steel.ini (3.5 KB) steel.rvb (10.7 KB)

Python is however case sensitive, so make sure everything matches.

Thank you. They are all caps in both the script and ini file, but the error message changes it to lower case…I’ll try all lower case when I get back to work.

@Helvetosaur

Something weird is going on here.

FLAT_BAR (ini) + FLAT_BAR (script) = fail
flat_bar (ini) + flat_bar (script) = success
flat_bar (ini) + FLAT_BAR (script) = success
FLAT_BAR (ini) + flat_bar (script) = fail
flatbar (ini) + flatbar (script) = success
FLATBAR (ini) + FLATBAR (script) = fail
flat_bar (ini) + FlAt_bAr (script) = success

So it looks like the ini file “section” must be lower case.

1 Like

Yep, looks like this was ‘designed in’… In the .ini file the entry can be caps or lower case or whatever, but when it is read via GetSettings(), the return is automatically converted to lower case… So if you want to match a string in your script, it needs to be lowercased as well.

Here is the relevant portion of the GetSettings() function from the rhinoscriptsyntax library:

    import ConfigParser
    try:
        cp = ConfigParser.ConfigParser()
        cp.read(filename)
        if not section: return cp.sections()
        section = string.lower(section)  #<-- converts to lower case
        if not entry: return cp.options(section)
        entry = string.lower(entry)  #<-- converts to lower case
        return cp.get(section, entry)
    except IOError:
        return scriptcontext.errorhander()
    return scriptcontext.errorhandler()

Now, as to why it was done this way - (my theory)

In vb Rhinoscript, things are not case sensitive, so any string containing a sequence of letters/numbers will match another string with the same sequence of letters/numbers, even if the caps/lower don’t match.

In Python this is not true, the casing needs to match exactly for two strings to be considered equal. So I think the return was specifically lowercased to facilitate matching, because you might never really know what the original .ini looks like in terms of how it was typed.

The main thing that is missing here perhaps is a mention in the Help file that the section and entry arguments are returned as lowercase thus any matching strings must be lowercase in order for this to work reliably… which is the opposite of vb Rhinoscript which specifically mentions the case-insensitivity of these entries in the Help file.

@dale, @alain - so should this be changed and the forced lowercase removed, or should the Help file have this additional information? BTW, the method for converting to lowercase with string.lower() is deprecated I think…

        cp = ConfigParser.ConfigParser()
        cp.read(filename)
        if not section: return cp.sections()
        section = string.lower(section)
        if not entry: return cp.options(section)
        entry = string.lower(entry)
        return cp.get(section, entry)

should maybe be

        cp = ConfigParser.ConfigParser()
        cp.read(filename)
        if not section: return cp.sections()
        if not entry: return cp.options(section.lower())
        return cp.get(section.lower(), entry.lower())

image
image

I’m not sure that’s right. My tests above indicate that the .ini section must be lower case and the script can be UPPER, lower or sTUdlY cAPs, as it currently stands. Are you seeing different behaviour?

It is confusing though so probably needs some updating. Deferring to strict case matching is probably my preference.

OK, I was simply inferring that from the code above. According to that, the conversion to lowercase takes place after the .ini file is read inside the GetSettings() function - so it always returns lowercase. From that I inferred that the .ini could have any kind of casing, but the output of GetSettings() would be lowercase. So if you want to see if the GetSettings output matches some other string in Python via if a==b:, that string also has to be lower case.

I will do some tests here later on.

OK, so here’s what’s happening - IMO the function is indeed broken.

def GetSettings(filename, section=None, entry=None):
    import ConfigParser
    try:
        cp = ConfigParser.ConfigParser()
        cp.read(filename)
        if not section: return cp.sections()

Assuming that the section name is something like [Test_Case]
Up to here it’s OK, as the section name exists, the ConfigParser returns the section name exactly as it is in the ini file, i.e. 'Test_Case'

Where it goes south is the next line:

        section = string.lower(section)
        if not entry: return cp.options(section)

What happens above is that the section name is lowercased, then it tries to use that lowercased name to re-look up the section in the ini and get its entries. Unfortunately, as in our example above, if the section name is not lowercased in the ini to begin with, it will not find the section because the cases do not match. So it tells you that this section does not exist.

So, the answer is, the ini can effectively be any case, but because the GetSettings() function is incorrectly written, the only thing that works right now is using lower case names exclusively for sections.

OTOH, casing doesn’t seem to matter for the entries under the section… The GetSettings() does indeed lowercase all the entry names, but the config parser uses a different method to look those up and that one seems to be case-insensitive… go figure.

So, if you want to make it work right now, you can actually edit the GetSettings function… You just need to comment out one line:

  import ConfigParser
    try:
        cp = ConfigParser.ConfigParser()
        cp.read(filename)
        if not section: return cp.sections()
        #section = string.lower(section)  #COMMENT OUT THIS LINE
        if not entry: return cp.options(section)
        entry = string.lower(entry)
        return cp.get(section, entry)
    except IOError:
        return scriptcontext.errorhander()
    return scriptcontext.errorhandler()

The file to edit is here:

C:\Users\<username>\AppData\Roaming\McNeel\Rhinoceros\6.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\rhinoscript\utility.py

The GetSettings() function starts at about line 374, the line to comment out should be at line 407.

In my tests here, if the casing (upper, lower, etc.) matches exactly, then it should work. If you want to make it case-insensitive, then it’s more work. It seems that this is more of a problem with the Python ConfigParser module than anything else…

Update -
Yes it is indeed a problem with the ConfigParser module, and actually no simple way to get it to be case-insensitive… I found this in StackOverflow, but I can’t get any of the outlined methods to work - probably just not Python savvy enough. @Alain?

Hi Mitch,
It looks like adding:

cp.optionxform = str

and commenting out the string.lower(...) calls makes it case-sensitive.

rs.GetSettings was probably written at a time when that ConfigParser option didn’t work as expected. In any case I’ve logged the issue.
Thanks!

1 Like

Hi Alain,

Thanks! I was trying to make it case-insensitive, but that seems hard.