Skip to content

Commit 815680d

Browse files
committed
update python lint and format
1 parent 7c1228d commit 815680d

File tree

1 file changed

+101
-62
lines changed

1 file changed

+101
-62
lines changed

docs/posts/2021/2021-01-04-python-lint-and-format.md

Lines changed: 101 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ categories:
77
comments: true
88
date:
99
created: 2021-01-04
10-
updated: 2025-02-22
10+
updated: 2025-08-31
1111
description: Some commands to lint and format Python files
1212
---
1313

@@ -17,12 +17,14 @@ description: Some commands to lint and format Python files
1717

1818
## Azure SDK Python Guidelines
1919

20-
[https://azure.github.io/azure-sdk/python_implementation.html](https://azure.github.io/azure-sdk/python_implementation.html)
20+
- [https://azure.github.io/azure-sdk/python_design.html](https://azure.github.io/azure-sdk/python_design.html)
21+
- [https://azure.github.io/azure-sdk/python_implementation.html](https://azure.github.io/azure-sdk/python_implementation.html)
2122

2223
## Lint
2324

2425
- Update 2023-05-21: Replaced flake8, pylint, black and isort by [ruff](https://github.com/charliermarsh/ruff). When replacing pylint, should [add check by mypy](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-pylint).
2526
- Update 2023-11-07: Bandit could be replaced by ruff too with the support of flake-bandit.
27+
- Update 2025-08-31: [Ty or Pyrefly](../2025/2025-02-01-python-type-hints.md#typing-tools) (both written in Rust) could be used for type checking.
2628

2729
!!! note
2830
The only thing ruff can't do at the moment is [type checking](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-mypy-or-pyright-or-pyre).
@@ -43,7 +45,7 @@ ruff check --ignore-noqa --exit-zero
4345
```
4446

4547
!!! note "try also `uv pip install`"
46-
For `pip install` users, try [uv pip install](https://github.com/astral-sh/uv) from the same author of ruff.
48+
For `pip install` users, try [uv pip install](https://github.com/astral-sh/uv) from the same author of ruff. [uv cheat sheet here](../2025/2025-08-26-python-uv-cheat-sheet.md).
4749

4850
A highly opinionated and subjective perspective, but finally, we have a real competitor (or even a replacement) of `pip install`. If you have a private PyPi index, all you need to setup is `export UV_DEFAULT_INDEX=$PIP_INDEX_URL`; otherwise, just add `uv` before `pip install` command.
4951

@@ -162,8 +164,54 @@ Found 0 vulnerabilities in 214 packages
162164

163165
Github has already provided, free of charge, the [vulnerable dependencies alert](https://docs.github.com/en/code-security/supply-chain-security/managing-vulnerabilities-in-your-projects-dependencies/about-alerts-for-vulnerable-dependencies).
164166

167+
### mypy
168+
169+
!!! note
170+
While mypy is significantly less performant than [modern typing tools written in Rust](../2025/2025-02-01-python-type-hints.md#typing-tools), it remains the gold standard for type checking in Python. As of September 2025, all Rust-based typing tools are still in preview stage.
171+
172+
For projects using SQLAlchemy, consider installing the `sqlalchemy-stubs` plugin to handle SQLAlchemy's dynamic class generation. And also django-stubs, pandas-stubs, types-setuptools, types-requests etc.
173+
174+
Just some personal notes on mypy, though [this post](../2025/2025-02-01-python-type-hints.md) based on the official MyPy documentation.
175+
176+
[mypy config file](https://mypy.readthedocs.io/en/stable/config_file.html):
177+
178+
```ini
179+
[mypy]
180+
ignore_missing_imports = True # We recommend using this approach only as a last resort: it's equivalent to adding a # type: ignore to all unresolved imports in your codebase.
181+
plugins = sqlmypy # sqlalchemy-stubs
182+
exclude = (?x)(
183+
^venv
184+
| ^build
185+
)
186+
```
187+
188+
running mypy:
189+
190+
```bash
191+
mypy .
192+
mypy . --exclude [a regular expression that matches file path]
193+
mypy . --exclude venv[//] # exclude venv folder under the root
194+
```
195+
196+
!!! warning
197+
When using mypy, it would be better to use mypy against to [all files in the project](https://github.com/python/mypy/issues/13916), but not some of them.
198+
199+
#### Auto discover missing stub files
200+
201+
```bash
202+
# search available stubs, and ask for installation,
203+
# that can be used to declared the stub packages into requirements files.
204+
mypy --install-types .
205+
206+
# for local quick test, we might not care about the requirements files.
207+
mypy --install-types --non-interactive .
208+
```
209+
165210
### pyright
166211

212+
!!! warning
213+
Replaced by [ty](#ty). More info about Python typing tools in this [post](../2025/2025-02-01-python-type-hints.md#typing-tools)
214+
167215
faster than mypy.
168216

169217
```bash
@@ -209,49 +257,11 @@ exclude = []
209257
- running pyright in other CI Solutions:
210258
<https://github.com/microsoft/pyright/blob/main/docs/ci-integration.md>
211259

212-
### mypy
213-
214-
For projects having sqlalchemy, we often install the `sqlalchemy-stubs` plugin as sqlalchemy uses some dynamic classes.
215-
216-
And also django-stubs, pandas-stubs, types-setuptools, types-requests etc.
217-
218-
[mypy config file](https://mypy.readthedocs.io/en/stable/config_file.html):
219-
220-
```ini
221-
[mypy]
222-
ignore_missing_imports = True # We recommend using this approach only as a last resort: it's equivalent to adding a # type: ignore to all unresolved imports in your codebase.
223-
plugins = sqlmypy # sqlalchemy-stubs
224-
exclude = (?x)(
225-
^venv
226-
| ^build
227-
)
228-
```
229-
230-
running mypy:
231-
232-
```bash
233-
mypy .
234-
mypy . --exclude [a regular expression that matches file path]
235-
mypy . --exclude venv[//] # exclude venv folder under the root
236-
```
237-
238-
!!! warning
239-
240-
When using mypy, it would be better to use mypy against to [all files in the project](https://github.com/python/mypy/issues/13916), but not some of them.
241-
242-
#### Auto discover missing stub files
243-
244-
```bash
245-
# search available stubs, and ask for installation,
246-
# that can be used to declared the stub packages into requirements files.
247-
mypy --install-types .
248-
249-
# for local quick test, we might not care about the requirements files.
250-
mypy --install-types --non-interactive .
251-
```
252-
253260
### pylyzer
254261

262+
!! warning
263+
Replaced by [ty](#ty). The author is working for ty now. More info about Python typing tools in this [post](../2025/2025-02-01-python-type-hints.md#typing-tools).
264+
255265
A fast, feature-rich static code analyzer (type checker) & language server for Python written in Rust. A possible mypy, pyright replacement in the future, currently it's still in the [early stage](https://github.com/mtshiba/pylyzer/issues/59#issuecomment-1851284715).
256266
257267
pylyzer [converts Python ASTs to Erg ASTs](https://github.com/mtshiba/pylyzer#how-it-works) and passes them to Erg's type checker. It then displays the results with appropriate modifications.
@@ -264,6 +274,16 @@ pylyzer single_python_file.py
264274
pylyzer
265275
```
266276

277+
### ty
278+
279+
Fast, feature-rich static code analyzer (type checker) & language server for Python written in Rust. It also has a competitor called [pyrefly](https://pyrefly.org/) from Meta. As of September 2025, both are in preview stage yet.
280+
281+
More info about Python typing tools in this [post](../2025/2025-02-01-python-type-hints.md#typing-tools)
282+
283+
### pyrefly
284+
285+
Like ty written in Rust maintained by Meta.
286+
267287
### ignore lint error in one line
268288

269289
!!! warning
@@ -277,9 +297,10 @@ pylyzer
277297
| bandit | (2 spaces)# nosec |
278298
| pyright | (2 spaces)# pyright: ignore [reportOptionalMemberAccess, reportGeneralTypeIssues] |
279299
| mypy | (2 spaces)# type: ignore |
300+
| ty | (2 spaces)# ty: ignore[unresolved-attribute] |
280301
| multiple linters | (2 spaces)# type: ignore # noqa: {errorIdentifier} # pylint: disable={errorIdentifier} |
281302

282-
To ignore Pylint within a code block
303+
To ignore Pylint within a code block:
283304

284305
```python
285306
@@ -417,9 +438,12 @@ Hereunder an example of `pyproject.toml` file in my [fastapi-demo](https://githu
417438
[tool.ruff]
418439
fix = true
419440
show-fixes = true
420-
lint.select = ["ALL"]
421-
lint.ignore = [
441+
442+
[tool.ruff.lint]
443+
select = ["ALL"]
444+
ignore = [
422445
# https://beta.ruff.rs/docs/rules/
446+
"T201", # print found # ! not used in prod
423447
"D", # pydocstyle
424448
"E501", # line too long, handled by black
425449
"B008", # do not perform function calls in argument defaults
@@ -432,6 +456,7 @@ lint.ignore = [
432456
"COM812", # missing-trailing-comma
433457
"ISC001", # single-line-implicit-string-concatenation
434458
]
459+
435460
[tool.ruff.lint.per-file-ignores]
436461
"tests/**/*.py" = [
437462
# at least this three should be fine in tests:
@@ -442,11 +467,14 @@ lint.ignore = [
442467
"tools/**/*.py" = ["ALL"]
443468
"migrations/**/*.py" = ["ALL"]
444469
"_local_test/**/*.py" = ["ALL"]
470+
445471
[tool.ruff.lint.isort]
446472
combine-as-imports = true
447473
force-wrap-aliases = true
474+
448475
[tool.ruff.lint.pylint]
449476
max-args = 8
477+
450478
[tool.ruff.lint.pep8-naming]
451479
classmethod-decorators = ["pydantic.validator"]
452480
@@ -501,17 +529,19 @@ init_forbid_extra = true
501529
init_typed = true
502530
warn_required_dynamic_aliases = true
503531
532+
[tool.ty.src]
533+
exclude = ["_local_test", "tools", "migrations", ".venv", "app_sqlalchemy_v1"]
504534
505535
[project]
506536
name = "fastapi-demo"
507537
dynamic = ["version", "dependencies", "optional-dependencies"]
508538
authors = [{ name = "Xiang ZHU", email = "xiang.zhu@outlook.com" }]
509539
description = "fastapi-demo"
510540
readme = "README.md"
511-
requires-python = ">=3.11,<3.13"
541+
requires-python = ">=3.12,<3.14"
512542
classifiers = [
513543
"Operating System :: POSIX :: Linux",
514-
"Programming Language :: Python :: 3.11",
544+
"Programming Language :: Python :: 3.12",
515545
"Topic :: Software Development :: Libraries :: Python Modules",
516546
]
517547
@@ -620,14 +650,13 @@ pre-commit init-templatedir ~/.git-template
620650
Hereunder an example of `.pre-commit-config.yaml` file in my [fastapi-demo](https://github.com/copdips/fastapi-demo/blob/main/.pre-commit-config.yaml) repo.
621651
622652
```yaml title=".pre-commit-config.yaml"
623-
## Installation:
653+
# Installation:
624654
# pip install pre-commit
625655
# pre-commit install
626-
# pre-commit autoupdate
627656
628657
repos:
629658
- repo: https://github.com/pre-commit/pre-commit-hooks
630-
rev: v5.0.0
659+
rev: v6.0.0
631660
hooks:
632661
- id: check-json
633662
exclude: devcontainer.json
@@ -636,6 +665,10 @@ repos:
636665
- id: end-of-file-fixer
637666
- id: trailing-whitespace
638667
- id: debug-statements
668+
# without specifying `language_version: python 3.12`,
669+
# it generates SyntaxError: invalid syntax for Python 3.12 new Generic syntax:
670+
# class BaseService[T: BaseSQLModel]:
671+
# language_version: python 3.12
639672
- id: requirements-txt-fixer
640673
- id: detect-private-key
641674
- id: mixed-line-ending
@@ -657,31 +690,37 @@ repos:
657690
- repo: https://github.com/pre-commit/pygrep-hooks
658691
rev: v1.10.0
659692
hooks:
660-
- id: python-check-blanket-type-ignore
693+
# - id: python-check-blanket-type-ignore
661694
- id: python-check-mock-methods
662695
- id: python-no-log-warn
663696
- id: python-use-type-annotations
664697
- repo: local
665698
hooks:
666-
- id: ruff
667-
name: ruff
699+
- id: ruff-check
700+
name: ruff-check
668701
entry: ruff check --force-exclude
669702
language: system
670-
types: [python, pyi]
703+
types_or: [python, pyi, jupyter]
671704
args: []
672705
require_serial: true
673706
- id: ruff-format
674707
name: ruff-format
675708
entry: ruff format --force-exclude
676709
language: system
677-
types: [python, pyi]
710+
types_or: [python, pyi, jupyter]
678711
args: []
679712
require_serial: true
680-
- id: pyright
681-
name: pyright
682-
entry: pyright
713+
# - id: pyright
714+
# name: pyright
715+
# entry: pyright
716+
# language: system
717+
# types_or: [python, pyi, jupyter]
718+
# args: []
719+
- id: ty
720+
name: ty
721+
entry: ty check
683722
language: system
684-
types: [python, pyi]
723+
types_or: [python, pyi, jupyter]
685724
args: []
686725
- id: pytest
687726
name: pytest
@@ -692,7 +731,7 @@ repos:
692731
always_run: true
693732
```
694733
695-
```yaml title="mypy instead of pyright in .pre-commit-config.yaml"
734+
```yaml title="use mypy in .pre-commit-config.yaml"
696735
repos:
697736
- repo: local
698737
hooks:

0 commit comments

Comments
 (0)