You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/learning/better_code/mypy/index.md
+253-2Lines changed: 253 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,9 +4,260 @@ Mypy è un **static type checker** su Python. Agisce come un Linter e consente d
4
4
5
5
Il codice con Mypy verifica quelli che vengono chiamati **type hints** su Python. Essendo Python un linguaggio non tipizzato la violazione del controllo di Mypy non provoca degli errori di interpretazione o di compilazione, ma causa semplicemente dei warning che possono essere ignorati o analizzati più nel dettagli dal programmatore all'interno dell'IDE.
6
6
7
-
## Python types
7
+
Mypy esegue tutti i controlli di tipo senza mai eseguire il codice. Si tratta di quello che viene definito uno **strumento di analisi statica** (questo statico è diverso dallo statico di "tipizzazione statica"), significa che lo strumento funziona non eseguendo il codice python, ma valutando la struttura del programma.
8
+
9
+
Questo vuol dire che se il vostro programma fa cose particolari come fare chiamate API o cancellare file sul vostro sistema, potete comunque eseguire mypy sui vostri file e non avrà alcun effetto sul mondo reale.
10
+
11
+
## Usare mypy
12
+
13
+
È possibile usare **mypy** da terminale, ad esempio:
14
+
15
+
```python
16
+
defdouble(n):
17
+
return n *2
18
+
19
+
num = double(21)
20
+
print(num)
21
+
```
22
+
23
+
Se lanciamo da terminale **mypy** ci aspettiamo un errore vero?
24
+
25
+
```bash
26
+
$ mypy test.py
27
+
Success: no issues found in 1 source file
28
+
```
29
+
30
+
Ma come puoi vedere non succede niente, questo perchè come abbiamo detto nel capitolo generale non si forza mai il typings su python, quindi anche mypy non ci forza ad aggiungere i tipi al nostro codice, ma si limita a controllare quando ci sono.
31
+
32
+
È tuttavia possibile customizzare mypy in modo che controlli anche questi errori, facendo:
33
+
34
+
```bash
35
+
$ mypy --disallow-untyped-defs test.py
36
+
test.py:1: error: Function is missing a returntype annotation
37
+
Found 1 error in 1 file (checked 1 source file)
38
+
```
39
+
40
+
Ci sono moltissimi `--disallow-` che si possono usare, ma esiste tuttavia un extra che li racchiude tutti: `--strict`. Usando questo argomento di effettua un controllo "stringente" dei tipi.
41
+
42
+
```bash
43
+
# per iniziare provare ad usare:
44
+
mypy --strict myfile.py
45
+
```
46
+
47
+
## Configurare mypy
48
+
49
+
Ci sono diversi modi per configurare Mypy, qui ne vedremo 2 principalmente:
50
+
51
+
- Con VSCode
52
+
- Configurazione di default con i file di configurazione
53
+
54
+
### Configurazione con VSCode
55
+
56
+
È possibile all'interno del proprio file nella repository `.vscode/settings.json` inserire i parametri di configurazione di Mypy, installando anche l'estensione dedicata sullo store.
57
+
58
+
Ad esempio:
59
+
60
+
```json
61
+
...
62
+
"python.linting.mypyEnabled": true,
63
+
"python.linting.mypyArgs": [
64
+
"--ignore-missing-imports",
65
+
"--follow-imports=silent",
66
+
"--show-column-numbers",
67
+
"--strict"
68
+
],
69
+
...
70
+
```
71
+
72
+
### Configurazione con mypy.ini
73
+
74
+
Questa è la modalità di default, è possibile creare un file: `mypy.ini` all'interno della propria repository, nel quale è possibile inserire alcune regole che vogliamo controllare e modalità di utilizzo di Mypy.
75
+
76
+
Per la definizione di tutte le regole e modalità di utilizzo ovviamente facciamo riferimento alla guida ufficiale.
Come puoi vedere è anche possibile su Mypy utilizzare dei plugin. Anche riguardo a questo facciamo riferimento alla guida ufficiale.
90
+
91
+
## Esempi di Types
92
+
93
+
Facciamo quindi una serie di esempi di Types che si possono controllare e verificare con Mypy
94
+
95
+
### Primitive
96
+
97
+
Quando parliamo di primitive, parliamo dei tipi di default che si trovano nel linguaggio, come ad esempio: `int`, `str`, `float`, `bool`, ...
98
+
99
+
Ad esempio:
100
+
101
+
```python
102
+
defdouble(n: int) -> int:
103
+
return n *2
104
+
105
+
106
+
num = double(21)
107
+
print(num)
108
+
```
109
+
110
+
Lanciando quindi mypy otteniamo
111
+
112
+
```bash
113
+
$ mypy --strict test.py
114
+
Success: no issues found in 1 source file
115
+
```
116
+
117
+
### Collections
118
+
119
+
Le collections sono anche chiamate **strutture dati** come ad esempio: `List`, `Dict`, `Set`, ...
120
+
121
+
Nonostante queste collections ricordano le strutture dati come liste, dizionari, set, etc... non sono esattamente gli stessi **builtin collection types** ovvero quelli utilizzati di default dal linguaggio, ma sono delle astrazioni costruite apposta per effettuare dei controlli da mypy.
122
+
123
+
Un esempio:
124
+
125
+
```python
126
+
from typing import List, Set
127
+
128
+
defunique_count(nums: List[int]) -> int:
129
+
"""counts the number of unique items in the list"""
130
+
uniques: Set[int] =set() # Manually added type information
A partire da Python 3.7 tuttavia è possibile anche importare le annotazioni, ovvero avere la possibilità all'inizio del file di usare i tipi builtin come generic. Da Python 3.9 in poi è necessario solamente importare future per importarli tutti.
153
+
Un esempio con Python 3.9
154
+
155
+
```python
156
+
from typing import List
157
+
import__future__
158
+
159
+
# With mypy
160
+
my_var: List[int]
161
+
162
+
## With future (normal python built-in)
163
+
my_var_normal: list[int]
164
+
```
165
+
166
+
Ci sono degli edge-cases dove questo non funziona, ma sono rari. Tutto questo è descritto all'interno del [PEP 585](https://peps.python.org/pep-0585/)
167
+
168
+
### Debug
169
+
170
+
Oltre a tipizzare il codice, si può anche usare **mypy** come strumento di **debugger**, ovvero quando leggi codice di altri o di cui non conosci il tipo, è possibile utilizzare la funzione: `reveal_type` che consente di rivelare il tipo di variabile che si utilizza all'interno del codice, un esempio:
171
+
172
+
```python
173
+
from typing import List, Set
174
+
175
+
defunique_count(nums: List[int]) -> int:
176
+
"""counts the number of unique items in the list"""
reveal_type(counts) # The special magic reveal_type method
186
+
```
187
+
188
+
```bash
189
+
$ mypy --strict test.py
190
+
test.py:12: note: Revealed type is 'builtins.int'
191
+
```
192
+
193
+
`reveal_type` è una funzione speciale di mypy che non è usata nel codice. Tuttavia è importante ricordarsi di **rimuovere questa funzione una volta finito il debugging e il check del codice**.
194
+
195
+
### Union e Optional
196
+
197
+
Fino ad ora abbiamo visto come forzare l'utilizzo di un tipo, ma possono capitare dei casi dove è necessario specificare più di un tipo di variabile per un determinato parametro o metodo.
198
+
199
+
Per fare questo è possibile utilizzare:
200
+
201
+
- Union: quando vogliamo dire che si possono usare più parametri
202
+
- Optional: quando questi tipe hints sono opzionali e non strettamente necessari
test.py:4: note: Revealed type is 'Union[builtins.str, builtins.list[builtins.str]]'
227
+
test.py:8: note: Revealed type is 'builtins.list[builtins.str]'
228
+
test.py:11: note: Revealed type is 'builtins.str'
229
+
```
230
+
231
+
### Any
232
+
233
+
Quando non sappiamo nello specifico un tipo di dato, come nel caso di una lettura di un file che può contenere diversi tipi, è possibile utilizzare in Mypy: `Any`.
234
+
235
+
`Any` in realtà quello che fa è disabilitare il type checking, consentendoci di gestire la variabile o l'oggetto come vogliamo.
Ovviamente ci sono dei concetti molto più avanzati e approfonditi, come i generics, l'uso dei typings nelle classi e moltissimo altro all'interno di Mypy.
254
+
255
+
Vi rimandiamo alla sezione di approfondimenti qui sotto per seguire i link che ti abbiamo messo per approfondire questo tema.
256
+
257
+
## Approfondimenti
8
258
9
259
Alcuni articoli per approfondire questi concetti
10
260
11
-
-[Type checking with Mypy](https://realpython.com/lessons/type-checking-mypy/)
12
261
-[The comprehensive guide to mypy](https://tushar.lol/post/mypy-guide/)
262
+
-[Type checking with Mypy](https://realpython.com/lessons/type-checking-mypy/)
263
+
-[Python type checking guide](https://realpython.com/python-type-checking/)
Ruff è uno dei Python Linter più veloci disponibili, grazie all'implementazione in Rust e può essere usato per rimpiazzare numerose librerie in Python in quanto contiene al suo interno numerosissime features.
4
+
5
+
Alcuni tool che consente di rimpiazzare:
6
+
7
+
- Flake8 (con le estensioni)
8
+
- Isort
9
+
- pyupgrade
10
+
- autoflake
11
+
- pep8-naming
12
+
- pydocstyle
13
+
14
+
## Features
15
+
16
+
Ruff è ancora in sviluppo e in cambiamento, quindi sicuramente si evolverà ancora nel corso del tempo, qui scriviamo alcune features interessanti e che possono essere utili.
17
+
18
+
### Velocità
19
+
20
+
Essendo Ruff anche uno strumento di analisi statica del codice è necessario che sia molto veloce ed efficiente per garantire una copertura e un controllo veloce nel nostro codice all'interno dell'IDE senza appesantire i processi, dover attendere o dover per forza lanciare il codice.
21
+
22
+
Se sei interessato ai benchmark ci sono alcuni benchmark [all'interno del sito ufficiale](https://docs.astral.sh/ruff/contributing/?ref=blog.jerrycodes.com#benchmarking-and-profiling)
23
+
24
+
### Supporto
25
+
26
+
Attualmente Ruff racchiude le features di almeno altri 50 Python Package, creando una collezione di almeno 700 regole di lint. Tutte queste regole sono rimplementate in Ruff.
27
+
28
+
All'interno della [documentazione ufficiale](https://docs.astral.sh/ruff/rules/?ref=blog.jerrycodes.com#rules) è possibile trovare l'elenco esaustivo delle regole che vengono implementate anche in altre librerie. Sarebbe inutile e troppo lungo descriverle tutte qui con esempi.
29
+
30
+
### Configurazione
31
+
32
+
È molto facile configurare Ruff, per farlo è possibile creare un file di configurazione dedicato all'interno della repository chiamato `ruff.toml` contenente tutte le regole e configurazioni. Tuttavia se utilizzi `pdm` o `poetry` è anche possibile inserire le regole all'interno dei file di configurazione `pyproject.toml` in modo da tenere in un unico file tutte le regole necessarie per la configurazione del progetto.
33
+
34
+
Per capire come configurare al meglio Ruff, la documentazione ufficiale offre degli ottimi spunti a riguardo:
-[Tutte le impostazioni disponibili](https://docs.astral.sh/ruff/settings/)
38
+
-[Elenco delle regole disponibili](https://docs.astral.sh/ruff/rules/)
39
+
40
+
### Integrazione con l'IDE
41
+
42
+
Vista la crescente popolarità di Ruff, lo strumento è stato integrato all'interno degli IDE più famosi come Pycharm e VSCode.
43
+
44
+
Su VSCode è possibile installare l'estensione dedicata che consente di utilizzare tutte le potenzialità dello strumento.
45
+
46
+
### Fix automatico del codice
47
+
48
+
Oltre a segnalare i problemi Ruff ha integrato uno strumento di **autofix** del codice.
49
+
50
+
È uno strumento molto potente che consente di applicare le regole di ruff e automaticamente cambiare il codice per adattarsi a queste regole.
51
+
52
+
Per farlo è necessario installare ruff e chiamare da terminale le sue funzionalità:
53
+
54
+
```bash
55
+
ruff check --fix .
56
+
57
+
# Lint all files in `/path/to/code` (and any subdirectories)
58
+
$ ruff check path/to/code/
59
+
60
+
# Lint all `.py` files in `/path/to/code`
61
+
$ ruff check path/to/code/*.py
62
+
63
+
# Lint `file.py`
64
+
$ ruff check path/to/code/to/file.py
65
+
```
66
+
67
+
### Ignorare gli errori nel codice
68
+
69
+
Come altri strumenti Ruff consente di ignorare direttamente degli errori nel codice, in un file o in una linea.
70
+
71
+
Questo metodo è ovviamente non consigliato, è sempre meglio cercare di risolvere tutte le problematiche.
72
+
73
+
Per farlo è necessario utilizzare il commento `# noqa`, si può usare specificando il tipo di errore (scelta consigliata) con il codice di riferimento, oppure senza specificare il codice in modo generico.
"""Given a list of integers, return the sum of all even numbers in the list."""
84
+
returnsum(num for num in numbers if num %2==0)
85
+
```
86
+
87
+
#### Ignorare errori all'interno di tutto il file
88
+
89
+
In questo modo verranno ignorati tutti gli errori di tipo `UP006` all'interno del file, ricordiamo che è sempre possibile non specificare il tipo di errore anche se non è consigliato, anche perchè a cosa serve altrimenti averlo installato e utilizzarlo?
90
+
91
+
```python
92
+
# ruff: noqa: UP006
93
+
from typing import List
94
+
95
+
defsum_even_numbers(numbers: List[int]) -> int:
96
+
"""Given a list of integers, return the sum of all even numbers in the list."""
97
+
returnsum(num for num in numbers if num %2==0)
98
+
```
99
+
100
+
#### Ignorare errori nel file di configurazione
101
+
102
+
```ini
103
+
# Enable flake8-bugbear (`B`) rules.
104
+
select = ["E", "F", "B"]
105
+
106
+
# Never enforce `E501` (line length violations).
107
+
ignore = ["E501"]
108
+
109
+
# Avoid trying to fix flake8-bugbear (`B`) violations.
110
+
unfixable = ["B"]
111
+
112
+
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
113
+
[per-file-ignores]
114
+
"__init__.py" = ["E402"]
115
+
"path/to/file.py" = ["E402"]
116
+
```
117
+
118
+
#### Ignorare particolari errori da command line
119
+
120
+
È anche possibile ignorare particolari errori quando si lancia da command line, facendo
121
+
122
+
```bash
123
+
$ ruff check path/to/code/ --ignore E402
124
+
```
125
+
126
+
## Consigli
127
+
128
+
Siccome Ruff è uno strumento molto potente e omnicomprensivo, sconsigliamo di creare una configurazione molto estesa fin da subito. È un processo che richiede tempo e tante modifiche prima di trovare una serie di regole che possono adeguarsi al progetto o alle regole all'interno di un team o di una organizzazione.
129
+
130
+
Se sei interessato ad un esempio di configurazione ti rimandiamo alla nostra configurazione che utilizziamo all'interno della [repository di PythonBiellaGroup, Bear](https://github.com/PythonBiellaGroup/Bear/blob/main/.ruff.toml)
0 commit comments