busty icon indicating copy to clipboard operation
busty copied to clipboard

Check type hints across the codebase

Open anoadragon453 opened this issue 2 years ago • 2 comments

Due to python's nature of ducktyping (lack of explicitly marking variables with strong types), it's possible to make small errors while programming that result in real exceptions. One example is https://github.com/anoadragon453/busty/pull/82, where it was assumed that message.author was always a Member. But in reality, it could either be a Member or a User.

Tools such as mypy can help catch these errors before they're discovered in production, by checking types and raising errors if a given operation (such as User.roles) is invalid.

Personally, I'm very familiar with using mypy as part of my projects, and find it an essential tool for writing maintainable python code. The downside of including it is that we need to ensure we add type hints where variables are ambiguous. However, I consider this more of a necessary investment than a negative point.

To test it out, simply do:

pip install mypy
mypy main.py

On current main, it will produce the following output:

expand output
(venv) [user@izzy busty]$ mypy main.py 
main.py:9: error: Skipping analyzing "mutagen": module is installed, but missing library stubs or py.typed marker
main.py:9: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
main.py:10: error: Skipping analyzing "mutagen.flac": module is installed, but missing library stubs or py.typed marker
main.py:11: error: Skipping analyzing "mutagen.id3": module is installed, but missing library stubs or py.typed marker
main.py:12: error: Skipping analyzing "mutagen.ogg": module is installed, but missing library stubs or py.typed marker
main.py:94: error: Item "User" of "Union[User, Member]" has no attribute "roles"
main.py:309: error: Item "None" of "Optional[Guild]" has no attribute "voice_channels"
main.py:309: error: Incompatible types in assignment (expression has type "Union[List[VoiceChannel], Any]", variable has type "List[Union[VoiceChannel, StageChannel]]")
main.py:310: error: Item "None" of "Optional[Guild]" has no attribute "stage_channels"
main.py:319: error: Item "None" of "Optional[Guild]" has no attribute "get_member"
main.py:319: error: Item "None" of "Optional[ClientUser]" has no attribute "id"
main.py:335: error: Item "None" of "Union[Member, None, Any]" has no attribute "edit"
main.py:349: error: Item "None" of "Union[Member, None, Any]" has no attribute "display_name"
main.py:482: error: Argument 1 to "scrape_channel_media" has incompatible type "Union[Union[TextChannel, Thread, DMChannel, PartialMessageable], GroupChannel]"; expected "TextChannel"
main.py:494: error: Need type annotation for "embed_description_list" (hint: "embed_description_list: List[<type>] = ...")
main.py:573: error: Incompatible types in assignment (expression has type "Union[Union[TextChannel, Thread, DMChannel, PartialMessageable], GroupChannel]", variable has type "Optional[TextChannel]")
main.py:607: error: Argument 1 to "save" of "Attachment" has incompatible type "str"; expected "Union[BufferedIOBase, PathLike[Any]]"
Found 16 errors in 1 file (checked 1 source file)

Note main.py:94: error: Item "User" of "Union[User, Member]" has no attribute "roles", which would've prevented #82.

If this sounds reasonable, I'll put up a PR to add in the necessary CI, deps and fixes.

anoadragon453 avatar Feb 12 '22 18:02 anoadragon453

That sounds like a good idea to me

Cephian avatar Feb 12 '22 18:02 Cephian

This is currently blocked on the rest of the codebase refactoring tasks, mainly that which eliminates all of our Optional global variables.

anoadragon453 avatar Aug 20 '22 18:08 anoadragon453