FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

abs(Union[int, Decimal]) is inferred as object · Issue #8601 · python/mypy · GitHub

Repository navigation

abs(Union[int, Decimal]) is inferred as object #8601

Open

Description

I'm afraid I'm not sure whether I'm reporting a bug or requesting a feature. I'm happy to leave that up for you all to triage. I'll start with what I'm seeing and what I'd like to see, and then get a little bit into the why.

Here's a minimal reproduction:

from decimal import Decimal
from typing import Union

def check(x: Decimal, y: Union[Decimal, int]) -> bool:
    return x < -abs(y)

With Python 3.7.3 (from Debian buster) and mypy 0.770 (from PyPI), checking this code returns an error:

deccomp.py:6: error: Unsupported operand type for unary - ("object")  [operator]
Found 1 error in 1 file (checked 1 source file)

The return type of abs is inferred as object, I think because that's the common supertype of int and Decimal. It would be nice if, one way or another, the return type of abs could be inferred as some higher numeric type.

The context here is I'm working on accounting software where I want to be careful to do decimal math throughout. In other words, I never want to deal with the decimal.FloatOperation signal. For functions that do basic arithmetic or comparisons across numbers, it's fine to accept arguments that are either int or Decimal, and it's convenient for callers if I can annotate that argument type rather than requiring them to convert their integer arguments to Decimal all the time.

Activity

  1. JelleZijlstra commented on Mar 30, 2020

    Member

    For context, the definition of abs in typeshed is

    def abs(__n: SupportsAbs[_T]) -> _T: ...

    Considering the definition of SupportsAbs, this means that abs returns whatever the __abs__ method on its argument type returns. int.__abs__ is declared as returning int in typeshed, and Decimal.__abs__ returns Decimal.

    I feel like mypy should be able to use these stubs to infer that abs(Union[int, Decimal]) returns Union[int, Decimal], but the current type inference isn't up to the task. There are probably already some similar issues.

  2. msullivan commented on Apr 3, 2020

    Collaborator

    Yeah, I agree that would probably be a better type in this case. But whether to infer a union type or a join is tricky and changing behavior will break code in places; not sure if this one has a fix that would be better.

    Fix for your particular issue might be to write a wrapper with a better type.

  3. brettcs commented on Apr 8, 2020

    Author

    I ended up just casting y to Decimal at the top of the function. I think y = Decimal(y) should always work given these types, so the cast let me tell mypy that with less runtime overhead.

  4. jvdwetering commented on Apr 21, 2020

    I think the following is a related example:

    from typing import Union
    from fractions import Fraction
    import math
    
    def f(a: Union[Fraction, int]) -> Union[Fraction, int]:
        return math.pi*a
    

    This gives the error on the last line:

    Incompatible return value type (got "Union[Any, float]", expected "Union[Fraction, int]")

    EDIT: to be clear, there is supposed to be an error, but I feel mypy should infer the type float instead of Union[Any, float]

  5. zormit commented on May 3, 2021

    It seems to me that python/typeshed#5275 is related too. There, @erictraut describes:

    The problem is that mypy uses a "join" operation to widen types in its type constraint solver rather than using a union operation.

    Are there any plans to fix this and change the way the constraint solver works?

    PS: This is the first issue that I dug up trough the search that seems to be related and I am new to this project. As was said here, there are probably related issues. Feel free to refer me to a more relevant issue/feature if this has been discussed.

  6. hauntsaninja commented on May 3, 2021

    Collaborator

    #5392 and #9264 are related, I think

  7. slafs commented on Jun 28, 2023

    Run into this bug today too.

    This snippet:

    from decimal import Decimal
    
    m = min(Decimal(1), 2)
    
    Decimal(m)

    produces:

    fiddles/decimal_mypy.py:3: error: Value of type variable "SupportsRichComparisonT" of "min" cannot be "object"  [type-var]
    fiddles/decimal_mypy.py:5: error: Argument 1 to "Decimal" has incompatible type "object"; expected "Decimal | float | str | tuple[int, Sequence[int], int]"  [arg-type]
    Found 2 errors in 1 file (checked 1 source file)
    

    using mypy 1.4.1 (compiled: yes) and Python 3.10.9.

    I've also read python/typeshed#5275 and it looks that this is fine with Pyright. Relevant quotes from that issue seem to be:

    The base problem is that mypy looks for a common base type in case like this, though, and only finds object

    and

    The problem is that mypy uses a "join" operation to widen types in its type constraint solver rather than using a union operation.

  8. injust commented on Dec 6, 2025

    In mypy 1.19.0, this gives you an inferred type of _typeshed.SupportsDunderLT[Any] | _typeshed.SupportsDunderGT[Any]:

    https://mypy-play.net/?mypy=latest&python=3.14&gist=b487d5c493c042c8c4a0669ffa0e6e5c

    from decimal import Decimal
    
    m = min(Decimal(1), 2)
    reveal_type(m)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL