Skip to content

[1.20 regression] _Environ.get(key, default) returns str | None when default is an alias to Any #21344

Open

Description

@pablogsal

Since mypy 1.20, passing a value whose type is an alias to Any (e.g. Incomplete: TypeAlias = Any) as the default argument of os.environ.get causes mypy to infer str | None instead of Any, producing a spurious [arg-type] error at the use site.

Reproducer: https://mypy-play.net/?gist=7a79f64ac9733542b571cc358f6d3165

import os
from pathlib import Path
from typing import Any
from typing_extensions import TypeAlias

Incomplete: TypeAlias = Any

A: Incomplete = Path()
reveal_type(A)               # Any  (good)

B = os.environ.get('TEST', A)
reveal_type(B)               # str | None  (bad)  (expected: Any)

C = Path(B)                  # error: Argument 1 to "Path" has incompatible
                             # type "str | None"; expected "str | PathLike[str]"

Bisected to 30260ca57 which added explicit _Environ.get/pop overloads where the first overload is def get(self, key, default: None = None) -> AnyStr | None. Before that sync, _Environ inherited Mapping.get, whose first overload took no default argument, so 2-arg calls couldn't match it.

Versions: broken on mypy ≥ 1.20.0; works on mypy ≤ 1.19.1.

Activity

  1. changed the title [-]_Environ.get(key, default) returns str | None when default is an alias to Any[/-] [+][1.20 regression] _Environ.get(key, default) returns str | None when default is an alias to Any[/+] on Apr 27, 2026
  2. hauntsaninja commented on Apr 27, 2026

    @hauntsaninja
    Collaborator

    Bisects to this typeshed sync #20505

  3. hauntsaninja commented on Apr 27, 2026

    @hauntsaninja
    Collaborator

    Looks like it'll be python/typeshed#15194

  4. hauntsaninja commented on Apr 27, 2026

    @hauntsaninja
    Collaborator

    So either typeshed needs to be changed, or the following behaviour of mypy needs to be changed:

    from typing import Any, Generic, TypeVar, overload
    from typing_extensions import TypeAlias
    
    _T = TypeVar("_T")
    
    class Env:
        @overload
        def get(self, key: str, default: None = None) -> str | None: ...
        @overload
        def get(self, key: str, default: str) -> str: ...
        @overload
        def get(self, key: str, default: _T) -> str | _T: ...
    
    Incomplete: TypeAlias = Any
    
    def main(unknown: Incomplete, env: Env):
        reveal_type(env.get('key', unknown))
    

    This is similar to e.g. pyright

  5. hauntsaninja commented on Apr 27, 2026

    @hauntsaninja
    Collaborator

    Reminds me of python/typeshed#13222

  6. pablogsal commented on Apr 27, 2026

    @pablogsal
    MemberAuthor

    This is the reason i opened #21345 (sorry for not linking it here)

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

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions