Back to Ruff

Custom unary operations

crates/ty_python_semantic/resources/mdtest/unary/custom.md

0.15.136.8 KB
Original Source

Custom unary operations

Class instances

py
class Yes:
    def __pos__(self) -> bool:
        return False

    def __neg__(self) -> str:
        return "negative"

    def __invert__(self) -> int:
        return 17

class Sub(Yes): ...
class No: ...

reveal_type(+Yes())  # revealed: bool
reveal_type(-Yes())  # revealed: str
reveal_type(~Yes())  # revealed: int

reveal_type(+Sub())  # revealed: bool
reveal_type(-Sub())  # revealed: str
reveal_type(~Sub())  # revealed: int

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `No`"
reveal_type(+No())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `No`"
reveal_type(-No())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `No`"
reveal_type(~No())  # revealed: Unknown

Classes

Dunder methods defined in a class are available to instances of that class, but not to the class itself. (For these operators to work on the class itself, they would have to be defined on the class's type, i.e. type.)

py
class Yes:
    def __pos__(self) -> bool:
        return False

    def __neg__(self) -> str:
        return "negative"

    def __invert__(self) -> int:
        return 17

class Sub(Yes): ...
class No: ...

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'Yes'>`"
reveal_type(+Yes)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'Yes'>`"
reveal_type(-Yes)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'Yes'>`"
reveal_type(~Yes)  # revealed: Unknown

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'Sub'>`"
reveal_type(+Sub)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'Sub'>`"
reveal_type(-Sub)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'Sub'>`"
reveal_type(~Sub)  # revealed: Unknown

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'No'>`"
reveal_type(+No)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'No'>`"
reveal_type(-No)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'No'>`"
reveal_type(~No)  # revealed: Unknown

Function literals

py
def f():
    pass

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `def f() -> Unknown`"
reveal_type(+f)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `def f() -> Unknown`"
reveal_type(-f)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `def f() -> Unknown`"
reveal_type(~f)  # revealed: Unknown

Subclass

py
class Yes:
    def __pos__(self) -> bool:
        return False

    def __neg__(self) -> str:
        return "negative"

    def __invert__(self) -> int:
        return 17

class Sub(Yes): ...
class No: ...

def yes() -> type[Yes]:
    return Yes

def sub() -> type[Sub]:
    return Sub

def no() -> type[No]:
    return No

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[Yes]`"
reveal_type(+yes())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[Yes]`"
reveal_type(-yes())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[Yes]`"
reveal_type(~yes())  # revealed: Unknown

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[Sub]`"
reveal_type(+sub())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[Sub]`"
reveal_type(-sub())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[Sub]`"
reveal_type(~sub())  # revealed: Unknown

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `type[No]`"
reveal_type(+no())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `type[No]`"
reveal_type(-no())  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `type[No]`"
reveal_type(~no())  # revealed: Unknown

Union where one member lacks the dunder

py
class Yes:
    def __pos__(self) -> bool:
        return False

    def __neg__(self) -> str:
        return "negative"

    def __invert__(self) -> int:
        return 17

class No: ...

def _(flag: bool):
    x = Yes() if flag else No()

    # snapshot: unsupported-operator
    reveal_type(+x)  # revealed: bool

    # snapshot: unsupported-operator
    reveal_type(-x)  # revealed: str

    # snapshot: unsupported-operator
    reveal_type(~x)  # revealed: int
snapshot
error[unsupported-operator]: Unary operator `+` is not supported for object of type `Yes | No`
  --> src/mdtest_snippet.py:17:17
   |
17 |     reveal_type(+x)  # revealed: bool
   |                 ^^
   |
info: `No` does not implement `__pos__`


error[unsupported-operator]: Unary operator `-` is not supported for object of type `Yes | No`
  --> src/mdtest_snippet.py:20:17
   |
20 |     reveal_type(-x)  # revealed: str
   |                 ^^
   |
info: `No` does not implement `__neg__`


error[unsupported-operator]: Unary operator `~` is not supported for object of type `Yes | No`
  --> src/mdtest_snippet.py:23:17
   |
23 |     reveal_type(~x)  # revealed: int
   |                 ^^
   |
info: `No` does not implement `__invert__`

Metaclass

py
class Meta(type):
    def __pos__(self) -> bool:
        return False

    def __neg__(self) -> str:
        return "negative"

    def __invert__(self) -> int:
        return 17

class Yes(metaclass=Meta): ...
class Sub(Yes): ...
class No: ...

reveal_type(+Yes)  # revealed: bool
reveal_type(-Yes)  # revealed: str
reveal_type(~Yes)  # revealed: int

reveal_type(+Sub)  # revealed: bool
reveal_type(-Sub)  # revealed: str
reveal_type(~Sub)  # revealed: int

# error: [unsupported-operator] "Unary operator `+` is not supported for object of type `<class 'No'>`"
reveal_type(+No)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `-` is not supported for object of type `<class 'No'>`"
reveal_type(-No)  # revealed: Unknown
# error: [unsupported-operator] "Unary operator `~` is not supported for object of type `<class 'No'>`"
reveal_type(~No)  # revealed: Unknown