- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.9k
 
Description
Noticed while looking into #12919, somewhat related to #7476
In configparser, SectionProxy acts as a view to a particular section from a RawConfigParser/ConfigParser. It implements various get*() methods (get(), getint(), getfloat(), etc.) which behave like the corresponding methods on RawConfigParser/ConfigParser, but with a notable exception: when the option being retrieved does not exist, all Section.get*() methods return None whereas the RawConfigParser.get*() methods raise an exception.
Here's an example using .getint():
import configparser
### Setup
config = configparser.ConfigParser()
config.read_string("""
[foo]
a = 1
""")
foo = config["foo"]
### Existing options (same behavior)
>>> config.getint("foo", "a")
1
>>> foo.getint("a")
1
### Non-existing options (*different* behavior)
>>> config.getint("foo", "b")
Traceback (most recent call last):
    ...
configparser.NoOptionError: No option 'b' in section: 'foo'
>>> foo.getint("b") is None
TrueThe reason for this discrepancy has to do with the fact that SectionProxy.get uses a default of fallback=None in CPython, whereas RawConfigParser.get uses a default of fallback=_UNSET. Presumably, this is done to make SectionProxy behave more like a Mapping. All other SectionProxy.get* methods inherit this behavior from SectionProxy.get.
Currently, the SectionProxy.get* methods use essentially the same overloads as RawConfigParser.get*, which (incorrectly) suggest that returning None isn't possible when fallback isn't specified:
typeshed/stdlib/configparser.pyi
Lines 315 to 318 in 355abb1
| @overload | |
| def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ... | |
| @overload | |
| def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ... | 
At first I thought to add | MaybeNone here, since changing the first overload to return | None has a high chance of being disruptive. However, in this case I wonder if returning | None might be preferable, since
- the likelihood of 
Nonebeing returned seems quite high (missing options are common), and - the migration path for affected code would be fairly straightforward (users can switch from 
section.get(x)tosection[x], or provide an explicitfallbackargument). 
Thoughts?