Skip to content

Overloading based on an argument which can be str or None doesn't seem to work #3805

Closed

Description

@OddBloke

When digging in to an issue with the stubs for configparser in the stdlib (python/typeshed#1527), I discovered that RawConfigParser.items() has two very different modes of operation. When it is given a section name (as a string, parser.items('name')), it will (with a little processing that shouldn't affect the types) return an iterable of tuples containing that sections keys and values (both of which are strings). When it is not given a section name (parser.items()), it instead returns an iterable of tuples containing the names and contents of the sections of the parser itself (the names are strings, and the contents are SectionProxy instances).

I have attempted to annotate this using overloading (because I believe that it's more precise than a union in this case):

@overload
def items(self, section: str = ..., raw: bool = ..., vars: _section = ...) -> Iterable[Tuple[str, str]]: ...

@overload
def items(self, section: None = ..., raw: bool = ..., vars: _section = ...) -> Iterable[Tuple[str, SectionProxy]]: ...

but I hit the following errors running ./tests/mypy_test.py in typeshed:

running mypy --python-version 3.6 --strict-optional # with 624 files
stdlib/3/configparser.pyi:103: error: Signature of "items" incompatible with supertype "Mapping"
stdlib/3/configparser.pyi:104: error: Overloaded function signatures 1 and 2 overlap with incompatible return types

The first one is expected (and, in fact, is the reason that this annotation has a #type: ignore on it in typeshed currently).

The second one, however, I don't expect because (with --strict-optional) I don't think None and str should overlap.

Activity

  1. OddBloke commented on Aug 7, 2017

    @OddBloke
    ContributorAuthor

    This is in the same realm as #3295, but as there's been a fair bit of discussion there, I didn't want to add to it without being sure.

  2. gvanrossum commented on Aug 7, 2017

    @gvanrossum
    Member

    I don't think it's related to #3295. The problem (with the example as written) is that both overloads can be called without arguments, so the error is correct. Note that the real function in fact does not work when called with None. Try using

        @overload
        def items(self, *, raw: bool = ...) -> AbstractSet[Tuple[str, _section]]: ...
        @overload
        def items(self, section: str, raw: bool = ...) -> Iterable[Tuple[str, str]]: ...
    

    but add back the other args.

    Also note that using AbstractSet makes the argument-less signature compatible with MutableMapping.items() so no # type: ignore is needed.

  3. OddBloke commented on Aug 7, 2017

    @OddBloke
    ContributorAuthor

    Well, that seems extremely obvious now you've pointed it all out. :-p Thanks for taking the time!

    I've updated the typeshed PR (and I'll consider filing some issues/PRs about improving the output of these warnings in the morning; I'll flatter myself by thinking I was only a couple of hints away from grokking this myself ;-).

  4. gvanrossum commented on Aug 8, 2017

    @gvanrossum
    Member

    Don't feel bad! I end up having to reinvent this regularly myself when it appears in a new context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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