Skip to content

configparser.SectionProxy.get* may return None #12933

Closed
@brianschubert

Description

@brianschubert

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
True

The 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:

@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

  1. the likelihood of None being returned seems quite high (missing options are common), and
  2. the migration path for affected code would be fairly straightforward (users can switch from section.get(x) to section[x], or provide an explicit fallback argument).

Thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions