Skip to content

Commit d3e2c33

Browse files
committed
add python type tool ty pyrefly diff
1 parent 6d3afa1 commit d3e2c33

File tree

2 files changed

+157
-3
lines changed

2 files changed

+157
-3
lines changed

docs/posts/2025/2025-02-01-python-type-hints.md

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@ If a directory contains both a `.py` and a `.pyi` file for the same module, the
490490

491491
Mypy also ships with two tools for making it easier to create and maintain stubs: [Automatic stub generation (stubgen)](https://mypy.readthedocs.io/en/stable/stubgen.html#stubgen) and [Automatic stub testing (stubtest)](https://mypy.readthedocs.io/en/stable/stubtest.html#stubtest).
492492

493-
494493
```bash title="use stubgen to generate stub files for package my_pkg_dir"
495494
# default output dir is: out, use -o to change it
496495
stubgen my_pkg_dir -o stubs
@@ -501,16 +500,53 @@ stubgen my_pkg_dir -o stubs
501500
pyright --createstub my_pkg_dir
502501
```
503502

504-
A common problem with stub files is that they tend to diverge from the actual implementation. Mypy includes the stubtest tool that can automatically check for discrepancies between the stubs and the implementation at runtime.
503+
A common problem with stub files is that they tend to diverge from the actual implementation. Mypy includes the [stubtest](https://mypy.readthedocs.io/en/stable/stubtest.html#stubtest) tool that can automatically check for discrepancies between the stubs and the implementation at runtime.
504+
505+
```bash
506+
MYPYPATH=stubs stubtest my_pkg_dir --concise
507+
```
508+
509+
## Generics
510+
511+
`list`, `set`, `dict`, etc, all the built-in collection classes are all Generics type, as they accept one or more type arguments within [...], which can be arbitrary types.
512+
For example, the type `dict[int, str]` has the type arguments `int` and `str`, and `list[int]` has the type argument `int`.
513+
514+
### Type variables with value restriction
515+
516+
[MyPy docs](https://mypy.readthedocs.io/en/stable/generics.html#type-variables-with-value-restriction):
517+
518+
```python title="Python 3.12 syntax"
519+
def concat[S: (str, bytes)](x: S, y: S) -> S:
520+
return x + y
521+
522+
concat('a', 'b') # Okay
523+
concat(b'a', b'b') # Okay
524+
concat(1, 2) # Error!
525+
```
526+
527+
```python title="Python 3.11 and earlier syntax"
528+
from typing import TypeVar
505529

530+
S = TypeVar('S', str, bytes)
506531

532+
def concat(x: S, y: S) -> S:
533+
return x + y
534+
535+
concat('a', 'b') # Okay
536+
concat(b'a', b'b') # Okay
537+
concat(1, 2) # Error!
538+
```
507539

508540
## Typing tools
509541

510542
### MyPy
511543

512544
Ref. MyPy in [this post](../2021/2021-01-04-python-lint-and-format.md#mypy).
513545

546+
While MyPy may not be the most performant type checker, particularly when integrated into pre-commit hooks, it remains an invaluable learning resource.
547+
The [MyPy documentation](https://mypy.readthedocs.io/en/stable/index.html) provides comprehensive guidance on writing effective type hints. Understanding its development history and current maintainership adds valuable context to its role in the Python ecosystem.
548+
And this posts is mainly based on MyPy documentation.
549+
514550
### Pyright && Pylance
515551

516552
Ref. Pyright in [this post](../2021/2021-01-04-python-lint-and-format.md#pyright).
@@ -540,4 +576,115 @@ While still in preview, it demonstrates the growing trend of high-performance Py
540576
The tool integrates smoothly with modern development environments through its [VSCode extension refly-vscode](https://marketplace.visualstudio.com/items?itemName=meta.pyrefly), making it accessible to a wide range of developers.
541577
Its backing by Meta suggests potential for robust development and long-term support.
542578

543-
Just a quick test, **pyrefly** seems to generate more typing errors than **ty**.
579+
### ty vs pyrefly
580+
581+
**pyrefly** has very similar output format to **ty**, but after a quick test, it seems that it generates more alerts than **ty** for the same codebase. It doesn't mean **pyrefly** is more powerful or more strict. Sometimes it just generates more false positives as it's still in preview.
582+
583+
And test on a single [Generics](https://mypy.readthedocs.io/en/stable/generics.html#generics) type, both **ty** and **pyrefly** generate the same type errors, but **ty** can point to the exact position of the error, while **pyrefly** only points to a limited position.
584+
585+
Test code:
586+
587+
```python title="mypy_demo.py" linenums="1"
588+
--8<-- "scripts/2025/mypy_demo.py"
589+
```
590+
591+
Test results:
592+
593+
=== "ty output"
594+
595+
```bash
596+
$ ty --version
597+
ty 0.0.1-alpha.14
598+
599+
$ ty check scripts/2025/mypy_demo.py
600+
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
601+
error[unsupported-operator]: Operator `+` is unsupported between objects of type `S` and `S`
602+
--> scripts/2025/mypy_demo.py:3:12
603+
|
604+
1 | # https://mypy.readthedocs.io/en/stable/generics.html#type-variables-with-value-restriction
605+
2 | def concat[S: (str, bytes)](x: S, y: S) -> S:
606+
3 | return x + y
607+
| ^^^^^
608+
4 |
609+
5 | concat('a', 'b') # Okay
610+
|
611+
info: rule `unsupported-operator` is enabled by default
612+
613+
error[invalid-argument-type]: Argument to function `concat` is incorrect
614+
--> scripts/2025/mypy_demo.py:7:8
615+
|
616+
5 | concat('a', 'b') # Okay
617+
6 | concat(b'a', b'b') # Okay
618+
7 | concat(1, 2) # Error!
619+
| ^ Argument type `Literal[1]` does not satisfy constraints of type variable `S`
620+
|
621+
info: Type variable defined here
622+
--> scripts/2025/mypy_demo.py:2:12
623+
|
624+
1 | # https://mypy.readthedocs.io/en/stable/generics.html#type-variables-with-value-restriction
625+
2 | def concat[S: (str, bytes)](x: S, y: S) -> S:
626+
| ^^^^^^^^^^^^^^^
627+
3 | return x + y
628+
|
629+
info: rule `invalid-argument-type` is enabled by default
630+
631+
error[invalid-argument-type]: Argument to function `concat` is incorrect
632+
--> scripts/2025/mypy_demo.py:7:11
633+
|
634+
5 | concat('a', 'b') # Okay
635+
6 | concat(b'a', b'b') # Okay
636+
7 | concat(1, 2) # Error!
637+
| ^ Argument type `Literal[2]` does not satisfy constraints of type variable `S`
638+
|
639+
info: Type variable defined here
640+
--> scripts/2025/mypy_demo.py:2:12
641+
|
642+
1 | # https://mypy.readthedocs.io/en/stable/generics.html#type-variables-with-value-restriction
643+
2 | def concat[S: (str, bytes)](x: S, y: S) -> S:
644+
| ^^^^^^^^^^^^^^^
645+
3 | return x + y
646+
|
647+
info: rule `invalid-argument-type` is enabled by default
648+
649+
Found 3 diagnostics
650+
```
651+
652+
=== "pyrefly output"
653+
654+
```bash
655+
$ pyrefly --version
656+
pyrefly 0.23.1
657+
658+
$ pyrefly check scripts/2025/mypy_demo.py
659+
ERROR `+` is not supported between `S` and `S` [bad-argument-type]
660+
--> /home/xiang/git/copdips.github.io/scripts/2025/mypy_demo.py:3:12
661+
|
662+
3 | return x + y
663+
| ^^^^^
664+
|
665+
Argument `S` is not assignable to parameter `self` with type `Self@bytes` in function `bytes.__add__`
666+
ERROR `+` is not supported between `S` and `S` [bad-argument-type]
667+
--> /home/xiang/git/copdips.github.io/scripts/2025/mypy_demo.py:3:12
668+
|
669+
3 | return x + y
670+
| ^^^^^
671+
|
672+
Argument `S` is not assignable to parameter `value` with type `Buffer` in function `bytes.__add__`
673+
ERROR `+` is not supported between `S` and `S` [no-matching-overload]
674+
--> /home/xiang/git/copdips.github.io/scripts/2025/mypy_demo.py:3:12
675+
|
676+
3 | return x + y
677+
| ^^^^^
678+
|
679+
No matching overload found for function `str.__add__`
680+
Possible overloads:
681+
(value: LiteralString, /) -> LiteralString [closest match]
682+
(value: str, /) -> str
683+
ERROR Returned type `bytes | Unknown` is not assignable to declared return type `S` [bad-return]
684+
--> /home/xiang/git/copdips.github.io/scripts/2025/mypy_demo.py:3:12
685+
|
686+
3 | return x + y
687+
| ^^^^^
688+
|
689+
INFO errors shown: 4, errors ignored: 0, modules: 1, transitive dependencies: 62, lines: 17,891, time: 0.12s, peak memory: physical 72.1 MiB
690+
```

scripts/2025/mypy_demo.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://mypy.readthedocs.io/en/stable/generics.html#type-variables-with-value-restriction
2+
def concat[S: (str, bytes)](x: S, y: S) -> S:
3+
return x + y
4+
5+
concat('a', 'b') # Okay
6+
concat(b'a', b'b') # Okay
7+
concat(1, 2) # Error!

0 commit comments

Comments
 (0)