I like the optional type idea:
opt(section_name, key_name, default_value, type)
So it can be written this way:
opt('section', 'key', None, StringType)
...but -- to answer your question -- the point here isn't really the 'singleness' of the factory function, but the fact that it is type-independent, which (in principle) would allow it to be extended to handle arbitrary types by delegating some functionality to the types themselves.
This is all a nice generalization, but I don't see that much use for it. There's only a handful of types that are worth supporting here. So the cost of the generalization isn't worth the benefits.
IMHO, it's just a different way to design it: instead of writing a specialized property handler for each supported type, use a standard interface provided by types themselves to handle the conversion to & from the string representation that is used in the config file. Unfortunately, this generalization is not as practical as I thought it would be.
Right, it pretty much only works for int, str, float, and for custome types that were designed with this usage in mind. So again I'm not sure that the generality is worth having.
Agreed. It depends a lot on the particular application, and to some extent, on personal taste. My own bias come from some of the applications that I have written, where I feared that the use of prefixes would end up cluttering the namespace.
But your "solution" is to introduce a mandatory extra namespace, which adds just as much typing/clutter to the code for your example (x.foo.bar instead of x.foo_bar) while forcing others who don't need it to use the extra namespace.
Besides the above mentioned communications software example (where I had several communication ports, each one stored into a section), another real scenario is the configuration of user interface parameters for several UI elements -- for example, font selection and background colors for specific forms. The same parameters ('font', 'font-color', 'background-color', etc.) are repeated for several sections. But surely, my own experience is limited, and I can't speak for other people on this point.
Most likely, you're hinting at a different use case, where there is an *arbitrary* set of sections, and you can't fix their number or names beforehand. In that case, using namespaces of the x.foo.bar form typically doesn't work well, because in your Python code, you can't hardcode foo -- rather, you have a variable whose contents loops over the different values of foo, and with this approach you'd end up doing getattr(x, sectionname).bar. In that case, I'd much rather have an alternative API where I can write x[sectionnname].bar, x.foo.bar now being equivalent to x["foo"].bar (note string quotes). As long as your section names aren't too weird (__getitem__ would be a bad section name :-) this should work well.
As an apart: I was *really* surprised when I first saw that this code worked:
Normally we write that as int('12') and float('-1e+12'). It was a new feature in Python 2.2; before that int wasn't the same thing as IntType.
The following test also worked, but I expected it to convert the value using an octal representation, but it didn't:
That's intentional; the int() constructor is often used to parse input from non-programming humans, who might cut and paste a number with leading zeros and not expect the zeros to turn the whole thing into octal. If you want this behavior, use int('012', 0).
And finally, the bool test:
Which obviously don't work because the 'False' string converts to True, as any other non-empty string do :-P. But it seemed a little bit weird when a I first saw it, specially when compared to FloatType and IntType.
The saying "a foolish consistency is the hobgoblin of little minds" applies to this kind of hypergeneralization.
Fortunately cats have nine lives. :-)