[PYTHON] Extract mypy errors that I fixed in conjunction with git

19 New graduates. From this year, I have been touching the server side and doing various things.

This time, I will talk about making it easy to understand the increase and decrease of error output of mypy in conjunction with git.

Preface: What is mypy

It is a tool that performs static analysis on type annotations in python. I recommend you to read the article of the ancestor. mypy will do it I did mypy

Introduction

The beginning of the matter was that the version of mypy in the deal was not updated for a long time (0.540). If you look at the Release Notes, it says Friday, 20 October 2017, etc.

When I tried to install the latest version locally with a light feeling, I got a lot of errors related to the check items added in the update. If you add the option to suppress it for the time being, it will pass, but ... this is ...

Let's fix

And I tried to fix the error at hand, but it became painful normally. Since errors are unevenly distributed throughout the project code, which is quite large, it is mentally painful that no improvement can be seen by just fixing it a little ...

It's impossible. Let's visualize ...

Had made

Visualization is an overstatement (because it is information that can be seen from the beginning), Wouldn't it be nice if the list of errors I crushed was displayed in an easy-to-understand manner? I thought. The policy is simple: run mypy twice before and after the change to get the diff. There is a policy that it should be possible to do something by calling the difference between the work tree and HEAD in conjunction with git. The execution time will be doubled, but on the other hand, mypy can be targeted only to the difference file, so I will go with wishful thinking that it is okay.

That's why I made it. https://github.com/fujita-ma/mymypy

There's no serious reason to write in rust, Since there was a lot of line-by-line text processing, I think it was easy to write iterator processing without stress. It's okay to use python, but to be honest, I wanted to do it in a language I didn't use at work.

Try using

Try it with the code in the mypy sample. http://www.mypy-lang.org/examples.html

main.py


class BankAccount:
    def __init__(self, initial_balance=0):
        self.balance = initial_balance
    def deposit(self, amount):
        self.balance += amount
    def withdraw(self, amount):
        self.balance -= amount
    def overdrawn(self):
        return self.balance < 0

my_account = BankAccount(15)
my_account.withdraw(5)
print(my_account.balance)

Let's run mypy.

❯ mypy --strict ./
main.py:2: error: Function is missing a type annotation
main.py:4: error: Function is missing a type annotation
main.py:6: error: Function is missing a type annotation
main.py:8: error: Function is missing a return type annotation
main.py:11: error: Call to untyped function "BankAccount" in typed context

I'm getting an error. After committing this once, modify some annotations and try running mypy and mymypy respectively.

main.py


class BankAccount:
    def __init__(self, initial_balance: int = 0) -> None:  # Annotated
        self.balance = initial_balance
    def deposit(self, amount):
        self.balance += amount
    def withdraw(self, amount):
        self.balance -= amount
    def overdrawn(self):
        return self.balance < 0

my_account = BankAccount(15)
my_account.withdraw(5)
print(my_account.balance)
❯ mypy --strict ./
main.py:4: error: Function is missing a type annotation
main.py:6: error: Function is missing a type annotation
main.py:8: error: Function is missing a return type annotation
main.py:12: error: Call to untyped function "withdraw" in typed context
❯ mymypy
main.py
-main.py:2:_: error: Function is missing a type annotation
main.py:4:4: error: Function is missing a type annotation
main.py:6:6: error: Function is missing a type annotation
main.py:8:8: error: Function is missing a return type annotation
-main.py:11:11: error: Call to untyped function "BankAccount" in typed context
+main.py:12:12: error: Call to untyped function "withdraw" in typed context

The crushed error is highlighted in red! Also, as a by-product, the added error is highlighted in green. It's common to fix an error and get another hidden error. I'll crush everything properly.

If you add all annotations properly, it will look like ↓.

main.py


class BankAccount:
    def __init__(self, initial_balance: int = 0) -> None:
        self.balance = initial_balance
    def deposit(self, amount: int) -> None:
        self.balance += amount
    def withdraw(self, amount: int) -> None:
        self.balance -= amount
    def overdrawn(self) -> bool:
        return self.balance < 0

my_account = BankAccount(15)
my_account.withdraw(5)
print(my_account.balance)
❯ mypy --strict ./

❯ mymypy
main.py
-main.py:2:_: error: Function is missing a type annotation
-main.py:4:_: error: Function is missing a type annotation
-main.py:6:_: error: Function is missing a type annotation
-main.py:8:_: error: Function is missing a return type annotation
-main.py:11:11: error: Call to untyped function "BankAccount" in typed context

I was able to crush 5 errors by my own work. I'm happy.

Try it in a suitable repository

Let's play a little. I'm going to handle revisions according to git diff, so if you specify an appropriate commit, you can get the difference between them. Take a peek at mypy's repository to catch commits.

a5005f4aa977e4911bce5c828fd707ca8680d592 The `inner_types` attribute seems to have no effect. I found a commit that seems to have been refactored.

Let's clone it and run it on mymypy.

❯ mymypy a5005f4~ a5005f4
mypy/checker.py
mypy/checker.py:64:64: error: Module 'mypy.semanal' has no attribute 'set_callable_name'
-mypy/checker.py:2813:_: error: Too many arguments for "PartialType"
-mypy/checker.py:2823:_: error: Too many arguments for "PartialType"
mypy/checker.py:2959:2959: error: unused 'type: ignore' comment
-mypy/checker.py:3022:_: error: "PartialType" has no attribute "inner_types"
-mypy/checker.py:3024:_: error: "PartialType" has no attribute "inner_types"
mypy/checker.py:4137:4133: error: unused 'type: ignore' comment
-mypy/checker.py:4311:_: error: "PartialType" has no attribute "inner_types"
mypy/checkexpr.py
mypy/checkexpr.py:203:203: error: unused 'type: ignore' comment
-mypy/checkexpr.py:592:_: error: "PartialType" has no attribute "inner_types"
-mypy/checkexpr.py:606:_: error: "PartialType" has no attribute "inner_types"
mypy/checkexpr.py:2368:2361: error: Returning Any from function declared to return "Optional[str]"
mypy/checkexpr.py:3003:2996: error: unused 'type: ignore' comment
mypy/type_visitor.py
mypy/type_visitor.py:167:167: error: unused 'type: ignore' comment
mypy/type_visitor.py:207:207: error: unused 'type: ignore' comment
mypy/type_visitor.py:229:229: error: unused 'type: ignore' comment
-mypy/type_visitor.py:293:_: error: "PartialType" has no attribute "inner_types"
mypy/types.py
mypy/types.py:190:190: error: unused 'type: ignore' comment
mypy/types.py:497:497: error: Returning Any from function declared to return "T"
mypy/types.py:520:520: error: Returning Any from function declared to return "T"
mypy/types.py:808:808: error: Returning Any from function declared to return "Union[Dict[str, Any], str]"
mypy/types.py:1557:1557: error: Returning Any from function declared to return "T"
mypy/types.py:1669:1669: error: Returning Any from function declared to return "T"
mypy/types.py:1789:1786: error: Returning Any from function declared to return "T"
mypy/types.py:1844:1841: error: unused 'type: ignore' comment
mypy/types.py:1889:1886: error: Returning Any from function declared to return "T"

You can see the effect of refactoring. Fun ✌ ('ω' ✌) Three ✌ ('ω') ✌ Three (✌'ω') ✌ (dead language)

in conclusion

I made it with an appropriate policy after a change of mood, but I am satisfied because it was able to be displayed with a good feeling. I will use it for my business because it is a big deal. ~~ If you actually use it, you will find many bugs ~~

Recommended Posts

Extract mypy errors that I fixed in conjunction with git
Note that I dealt with HTML in Beautiful Soup
Work memo that I tried i18n with Flask app
I registered PyQCheck, a library that can perform QuickCheck with Python, in PyPI.
I tried to predict the horses that will be in the top 3 with LightGBM
I made a familiar function that can be used in statistics with Python