From f0d6539d3d16440ecfd911a9b09718f9dc1b39be Mon Sep 17 00:00:00 2001 From: matejcik Date: Tue, 28 Nov 2023 14:57:39 +0100 Subject: [PATCH] feat: custom Pylint plugin for now, it catches the following incorrect function: async def show_foo() -> Awaitable[None]: return show_something_else("foo") because to correctly show the result, the caller would have to "await (await show_foo())" (this should either be "async def show_foo() -> None", or "def show_foo() -> Awaitable[None]") --- poetry.lock | 29 +++++++++++++++---- pyproject.toml | 1 + tools/trezor-pylint-plugin/.gitignore | 2 ++ tools/trezor-pylint-plugin/README.md | 23 +++++++++++++++ tools/trezor-pylint-plugin/pyproject.toml | 15 ++++++++++ .../trezor_pylint_plugin.py | 29 +++++++++++++++++++ 6 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 tools/trezor-pylint-plugin/.gitignore create mode 100644 tools/trezor-pylint-plugin/README.md create mode 100644 tools/trezor-pylint-plugin/pyproject.toml create mode 100644 tools/trezor-pylint-plugin/trezor_pylint_plugin.py diff --git a/poetry.lock b/poetry.lock index 3168ccce4..04debb602 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "astroid" @@ -1550,7 +1550,7 @@ testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psu [[package]] name = "trezor" -version = "0.13.8" +version = "0.13.9" description = "Python library for communicating with Trezor Hardware Wallet" optional = false python-versions = ">=3.6" @@ -1568,17 +1568,34 @@ requests = ">=2.4.0" typing_extensions = ">=3.10" [package.extras] -ethereum = ["rlp (>=1.1.0)", "web3 (>=4.8)"] +ethereum = ["rlp (>=1.1.0)", "web3 (>=5)"] extra = ["Pillow"] -full = ["Pillow", "PyQt5", "hidapi (>=0.7.99.post20)", "rlp (>=1.1.0)", "stellar-sdk (>=4.0.0,<6.0.0)", "web3 (>=4.8)"] +full = ["Pillow", "PyQt5", "hidapi (>=0.7.99.post20)", "rlp (>=1.1.0)", "stellar-sdk (>=6)", "web3 (>=5)"] hidapi = ["hidapi (>=0.7.99.post20)"] qt-widgets = ["PyQt5"] -stellar = ["stellar-sdk (>=4.0.0,<6.0.0)"] +stellar = ["stellar-sdk (>=6)"] [package.source] type = "directory" url = "python" +[[package]] +name = "trezor-pylint-plugin" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.8" +files = [] +develop = true + +[package.dependencies] +astroid = "*" +pylint = "*" + +[package.source] +type = "directory" +url = "tools/trezor-pylint-plugin" + [[package]] name = "typing-extensions" version = "4.0.1" @@ -1744,4 +1761,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "b8063d1d5d0a67a71bbcba204e0e542fbab5ea2bbe7b05c41f77b8e0fa736f0e" +content-hash = "72019b6396c9c15ee87968b735b954908a9e9e89d0c828b1e115293a0efb9f05" diff --git a/pyproject.toml b/pyproject.toml index 09ff9078c..24c333c2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ vulture = "^2.6" # tools binsize = "^0.1.3" toiftool = {path = "./python/tools/toiftool", develop = true, python = ">=3.8"} +trezor-pylint-plugin = {path = "./tools/trezor-pylint-plugin", develop = true} [tool.poetry.dev-dependencies] scan-build = "*" diff --git a/tools/trezor-pylint-plugin/.gitignore b/tools/trezor-pylint-plugin/.gitignore new file mode 100644 index 000000000..cd3cd5381 --- /dev/null +++ b/tools/trezor-pylint-plugin/.gitignore @@ -0,0 +1,2 @@ +*.egg-info +/build diff --git a/tools/trezor-pylint-plugin/README.md b/tools/trezor-pylint-plugin/README.md new file mode 100644 index 000000000..56044ab35 --- /dev/null +++ b/tools/trezor-pylint-plugin/README.md @@ -0,0 +1,23 @@ +# Custom Pylint rule checker + +For now, it catches the following problem (`async-awaitable-return`): + +```python +async def show_foo() -> Awaitable[None]: + return show_something("foo") +``` + +This is almost certainly a mistake -- the caller would need to say `await (await +show_foo())` to actually show the foo. + +The function should be one of: + +```python +async def show_foo() -> None: + return await show_something("foo") + +# ... or ... + +def show_foo() -> Awaitable[None]: + return show_something("foo") +``` diff --git a/tools/trezor-pylint-plugin/pyproject.toml b/tools/trezor-pylint-plugin/pyproject.toml new file mode 100644 index 000000000..bfddd5262 --- /dev/null +++ b/tools/trezor-pylint-plugin/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "trezor-pylint-plugin" +version = "0.1.0" +description = "" +authors = ["matejcik "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.8" +pylint = "*" +astroid = "*" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tools/trezor-pylint-plugin/trezor_pylint_plugin.py b/tools/trezor-pylint-plugin/trezor_pylint_plugin.py new file mode 100644 index 000000000..4c5879d3d --- /dev/null +++ b/tools/trezor-pylint-plugin/trezor_pylint_plugin.py @@ -0,0 +1,29 @@ +from astroid import AsyncFunctionDef +from pylint.checkers import BaseChecker +from pylint.checkers.utils import check_messages +from pylint.interfaces import IAstroidChecker + + +class AsyncAwaitableChecker(BaseChecker): + __implements__ = IAstroidChecker + + name = "async-awaitable-checker" + priority = -1 + msgs = { + "W9999": ( + 'Async function "%s" is likely not meant to return an Awaitable.', + "async-awaitable-return", + "Used when an async function returns an Awaitable instead of the result.", + ), + } + + @check_messages("async-awaitable-return") + def visit_asyncfunctiondef(self, node: AsyncFunctionDef): + # Check if the return type is explicitly an Awaitable + if node.returns and "Awaitable" in node.returns.as_string(): + self.add_message("async-awaitable-return", node=node, args=(node.name,)) + + +def register(linter): + """Required method to auto register this checker.""" + linter.register_checker(AsyncAwaitableChecker(linter))