Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve inference by enabling --protocols by default. #58

Open
gpshead opened this issue May 20, 2017 · 3 comments
Open

Improve inference by enabling --protocols by default. #58

gpshead opened this issue May 20, 2017 · 3 comments
Labels
cat: protocols Protocol (duck typing) enhancement

Comments

@gpshead
Copy link
Contributor

gpshead commented May 20, 2017

The example below is inferring amount as Iterable and claiming that balance is an unknown attribute as well as claiming that Account.deposit returns a list.

Potentially multiple different issues. Or just a need for actual documentation to describe the proper getting started code base bootstrapping process and expected odd results on entirely un-annotated code. :)

#!/usr/bin/python3

from typing import Dict, Optional, Union


class Account:
    """Only a horrible bank uses a float for account balances."""
    def __init__(self, opening_balance: float = 0.0):
        self.balance = opening_balance  # expect float

    def deposit(self, amount):  # expect (float) -> float
        self.balance += amount
        return self.balance


class Bank:
    def __init__(self, bank_owner="Unladen Swallows"):
        self._accounts = {}  # expect Dict[str, Account]
        self._accounts[bank_owner] = Account(3.5)

    def deposit(self, name, amount):  # expect (float) -> Optional[float]
        """Multiple return types is a horrible API!"""
        account = self._accounts.get(name)
        if account:
            return account.deposit(amount)
        else:
            return None


def main():
    bank = Bank()
    balance = bank.deposit("Unladen Swallows", 5)
    print("balance:", balance)
    assert balance == 8.5


if __name__ == '__main__':
    main()
rainbow_unicorn$ pytype -V 3.5 --output - example.py 
from typing import Any, Dict, Iterable, Optional

class Account:
    __doc__ = ...  # type: str
    balance = ...  # type: Any
    def __init__(self, opening_balance: float = ...) -> Any: ...
    def deposit(self, amount: Iterable) -> list: ...

class Bank:
    _accounts = ...  # type: Dict[Any, Account]
    def __init__(self, bank_owner = ...) -> None: ...
    def deposit(self, name, amount: Iterable) -> Optional[list]: ...


def main() -> None: ...
File "example.py", line 12, in deposit: No attribute 'balance' on Account [attribute-error]
@matthiaskramm matthiaskramm changed the title Odd inference results: Iterable and list vs float, unexpected attribute-error. Odd inference on Python 3 May 22, 2017
@matthiaskramm
Copy link
Contributor

This seems to only happen under Python 3. Under Python 2 (with backported type annotations), the inference looks correct, i.e., balance and amount are, and deposit returns, a Union[complex, float].

In general, our Python 3 compatibility is still WIP.

@gvanrossum
Copy link

(CC @ddfisher :-)

@rchen152
Copy link
Contributor

rchen152 commented Sep 26, 2018

This no longer seems to be Python 3-specific, since I now get identical results in 2 and in 3:

from typing import Any, Dict

class Account:
    __doc__ = ...  # type: str
    balance = ...  # type: Any
    def __init__(self, opening_balance: float = ...) -> None: ...
    def deposit(self, amount) -> Any: ...

class Bank:
    _accounts = ...  # type: Dict[Any, Account]
    def __init__(self, bank_owner = ...) -> None: ...
    def deposit(self, name, amount) -> Any: ...


def main() -> None: ...

The incorrect Iterable and list are gone, but balance is still Any. That's happening because we stopped trying to infer parameter types, so doing
self.balance += amount, where amount is an unknown, causes the type of balance to become unknown as well.

Passing the --protocols flag gets us more precise types:

from typing import Any, Dict, Optional, Union

class Account:
    __doc__ = ...  # type: str
    balance = ...  # type: Union[complex, float]
    def __init__(self, opening_balance: float = ...) -> None: ...
    def deposit(self, amount: complex) -> Union[complex, float]: ...

class Bank:
    _accounts = ...  # type: Dict[Any, Account]
    def __init__(self, bank_owner = ...) -> None: ...
    def deposit(self, name, amount: complex) -> Optional[Union[complex, float]]: ...


def main() -> None: ...

So I think the way to fix this would be to make --protocols the default.

@rchen152 rchen152 changed the title Odd inference on Python 3 Improve inference by enabling --protocols by default. Sep 26, 2018
@rchen152 rchen152 added the cat: protocols Protocol (duck typing) label Jan 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cat: protocols Protocol (duck typing) enhancement
Projects
None yet
Development

No branches or pull requests

4 participants