Skip to content

Mypy fails to determine function's type in icontract.ensure() #243

@claudio-ebel

Description

@claudio-ebel

Bug Report
When a type-annotated function is (re-)used in its own postcondition icontract.ensure decorator, mypy is not able to determine its type. Since mypy does successfully infers the types of an annotated function before the function's actual definition, this bug is part of the icontract library and not mypy.

To Reproduce

  1. Create a file “bug.py” with this content:
    # ––– file: bug.py –––
    from icontract import ensure
    
    
    @ensure(lambda a, b, result: result == myadd(b, a), "Commutativity violated!")
    def myadd(a: int, b: int) -> int:
        return a + b
  2. Use mypy on it
    $ mypy --config-file= bug.py 
    bug.py:5: error: Cannot determine type of "myadd"
    Found 1 error in 1 file (checked 1 source file)

Expected Behavior
In general, mypy does not have a problem with using an annotated function before its definition. To see this,

  1. Create a file “no_bug.py” with this content:
    # ––– file: no_bug.py –––
    
    
    def use_before() -> int:
        return myadd(1, 2)  # used before def; like in bug.py
    
    
    def myadd(a: int, b: int) -> int:
        return a + b
  2. Use mypy on it
    $ mypy --config-file= no_bug.py 
    Success: no issues found in 1 source file

Failed solution attempts
Using a forward declaration of myadd did not help:

  1. Create a file “forward_decl.py” with this content:
    # ––– file forward_decl.py ––
    from icontract import ensure
    from typing import Callable, cast
    
    MyaddType = Callable[[int, int], int]
    
    
    @ensure(lambda a, b, result: result == cast(MyaddType, myadd)(b, a),
            "Commutativity violated!")
    def myadd(a: int, b: int) -> int:
        return a + b
  2. Use mypy on it
    $ mypy --config-file= forward_decl.py
    forward_decl.py:8: error: Cannot determine type of "myadd"
    Found 1 error in 1 file (checked 1 source file)

An alternative use of a cast of myadd's return type did not help either:

  1. Create a file “return_type.py” with this content:
    # ––– file: return_type.py –––
    from icontract import ensure
    from typing import cast
    
    
    @ensure(lambda a, b, result: result == cast(bool, myadd(b, a)),
            "Commutativity violated!")
    def myadd(a: int, b: int) -> int:
        return a + b
  2. Use mypy on it
    $ mypy --config-file= return_type.py
    return_type.py:6: error: Cannot determine type of "myadd"
    Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: mypy 0.950 (compiled: yes)
  • Mypy command-line flags: --config-file=
  • icontract version used: icontract==2.6.1
  • Python version used: Python 3.10.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions