Bon timing avec l'article de Sam et Max (la débâcle de async en 3.7), j'ai récemment activé les warnings par défaut via cette variable d'environnement dans mon .zshrc :
$ export PYTHONWARNINGS="once"
Et quelle horreur ! Je me suis fait spammer comme jamais ☠
Donc j'ai entrepris un gros nettoyage de printemps de l'écosystème Python.
Fixture Pytest
Pour faciliter les recherches et les tests, je me suis servi de cette fixture pytest dès que possible (à placer dans conftest.py):
import pytest
@pytest.fixture(autouse=True)
def no_warnings(recwarn):
"""Fail on warning."""
yield
warnings = []
for warning in recwarn: # pragma: no cover
message = str(warning.message)
if (
# ImportWarning: Not importing directory "..." missing __init__(.py)
"Not importing directory" in message
# or "" in message
):
continue
warnings.append("{w.filename}:{w.lineno} {w.message}".format(w=warning))
assert not warnings
Cette fonction fera planter le test, même s'il était censé passer, en cas de warning.
ResourceWarning: unclosed file
Ça arrive lorsqu'un descripteur de fichier est toujours ouvert quand le garbage collector tente de libérer les ressources.
Voici plusieurs fonctions qui renvoient le contenu d'un fichier :
def func1(file: str) -> bytes:
"""Le descripteur de fichier n'est jamais fermé."""
return open(file, "rb").read()
def func2(file: str) -> bytes:
"""En soi, ce code est correct, mais si une erreur survient entre .open() et .close() alors il y aura un leak."""
f = open(file, "rb")
content = f.read()
f.close()
return content
def func3(file: str) -> bytes:
"""Code correct utilisant le bloc try...finally. Il n'y aura jamais de leak."""
f = open(file, "rb")
try:
return f.read()
finally:
f.close()
def func4(file: str) -> bytes:
"""La bonne façon de faire, c'est pythonique. Quel que soit ce qui arrive, il n'y aura jamais de leak."""
with open(file, "rb") as f:
return f.read()
Seule func4 devrait être utilisée. De manière générale, lorsqu'un object peut être utilisé va le context manager with, préférez cette forme.
func3 fait tout aussi bien le boulot, mais je trouve plus jolie et moins verbeuse la version avec context manager.
Modules patchés : aten, dotenv, dukpy, fire, fonttools, hypothesis, jenkinsapi, jira, joblib, keras, lazydata, marisa-trie, monkeytype, mss (1, 2), napalm, pathtools3, ply, pypager, pyte, pytest-rerunfailures, python-chess, python-prompt-toolkit, pytorch, pyxdg, questionay, requests, sorl-thumbnail, sphinx-notfound-page, startlette, stegano, strictyaml, tensorflow, tinytag, toml, tornado, tuna, watchdog.
Projets patchés : algorithms, apt-listchanges, bandit, bashplotlib, cpython (1, 2), cronos, friture, httpbin, home-assistant, httpie, ipython, medusa, mitmproxy, nikola, nuitka (1, 2), pip, pipenv, pre-commit, ptpython, pychess (1, 2), pygments, pyinstaller (1, 2, 3), ptterm, pymux, pyvim, reportbug, send2trash, tinyaes-py, tox, virtualenv, xonsh.
ResourceWarning: subprocess XXX is still running
Dans la même lignée que ResourceWarning: unclosed file, mais pour les processus encore en cours.
# Chacune de ces fonctions est une bonne façon de faire
def func1():
with subprocess.Popen(...) as p:
return p.stdout.read()
def func2():
return subprocess.check_output(...)
Il est aussi possible d'appeler
p.wait()
mais il se peut qu'il y ait des ResourceWarning: unclosed file. Mieux vaut privilégier le context manager quand possible.Projets patchés : nuxeo-drive, pyqt5.
ResourceWarning: unclosed socket.socket
Dans la même lignée que ResourceWarning: unclosed file, fermer les socket encore ouverts est un peu plus difficile. Si vous souhaitez jeter un œil, il y a de quoi faire avec HTTPie et requests.
Modules patchés : jira.
DeprecationWarning: invalid escape sequence
(
SyntaxWarning
à partir de Python 3.8)(
SyntaxError
à partir de Python 3.9 [ou 4.0])La source du problème est comment Python interprète le contenu des chaînes de caractère. Quand il y a un antislash, il va tenter de comprendre s'il s'agit d'un caractère unicode, hexadécimal ou un caractère spécial. Donc s'il y a un antislash et qu'il n'y a pas de séquence particulière à déchiffrer, ou même pour les regexp, il faut préfixer le code avec
r
ou br
:regexp = re.compile(r"(d+)")
regexp_b = re.compile(br"(d+)")
path = r"C:WindowsSystem32"
def func() -> None:
r"""Code qui ne fait rien ¯_(ツ)_/¯"""
return None
À noter que
rb
n'est pas valide en Python 2, donc br
pour tout le monde.Afin de dénicher ce genre d'erreur, vous pouvez utiliser cette commande :
$ python -Wall -m compileall -q -f *
Modules patchés : appkernel, cairo-lang, cntk, crayons, docker-py (1, 2), jira, js2py, keras, networkx, parse, pandas, pathtools3, poetry, prettytable, protobuf, psutil, pycryptodome (1, 2, 3), pyobjc, pypac, pyrax, pyte, python-chess, python-prompt-toolkit, python-toolbox, pytorch, pywinauto, scikit-learn, selenium, sunpy, tinytag.
Projets patchés : algorithms, apt-listchanges, bandit, briefcase, certbot, cpython (1, 2, 3), emscripten, enki, httpecho, http-prompt, ipython, mitmproxy, mpp-solar, mypy, naomi, nikola, pdoc, pep8speaks, pss, pychess, pyglossary, pyinstaller, pympler, pytest-xdist, slither, thefuck, waf, zeronet.
BytesWarning: comparison between bytes and string
Erreur typique que tout le monde a du gérer lors du passage de Python 2 à Python 3 :
if "abc" == b"abc":
print("Ce code ne sera jamais exécuté car la condition sera toujours fausse :boom:")
Vous pouvez utiliser l'argument -bb pour aider à détecter ce genre d'erreurs :
python -bb ARGS
Modules patchés : nuxeo, tzlocal, watchdog, watchdog3.
Projets patchés : reportbug.
Autres Résultats
- DeprecationWarning - 'xxx' is passed as positional argument : ebook-reader-dict (1, 2, 3)
- DeprecationWarning - an integer is required (got type float) : nuxeo-drive, watermark-me.
- ResourceWarning - unclosed scandir iterator : xonsh.
- DeprecationWarning - using or importing the ABCs from 'collections' instead of from 'collections.abc' : marisa-trie, nuitka, nuxeo, watchdog, watchdog3.
- DeprecationWarning - type argument to addoption() is a string 'choice' : pytest-timeout.
- DeprecationWarning - the imp module is deprecated in favour of importlib : pip, pycryptodome, watchdog, watchdog3.
- DeprecationWarning - inspect.getargspec() is deprecated : napalm, pre-commit, thermalprinter, xonsh.
- DeprecationWarning - open() 'U' mode is deprecated : fire, pss, pyinstaller (1, 2), reportbug.
- DeprecationWarning - BaseException.message has been deprecated : nuxeo.
- DeprecationWarning - the 'logging.warn' method is deprecated, use 'warning' instead : appkernel, jenkinsapi, mpp-solar, pip, pyglossary.
- DeprecationWarning - Please use assertEqual instead : algorithms, jenkinsapi, nuxeo-drive, pss, pyup.
- DeprecationWarning: This method will be removed in future versions. Use 'parser.read_file()' instead : pychess.
- DeprecationWarning: typing.re is deprecated, import directly from typing instead : pyglossary.
Statistiques
Combien de patches ont été mergé ?
- 2024: 4 (+20 -20)
- 2023: 3 (+19 -21)
- 2022: 9 (+122 -119)
- 2021: 8 (+49 -43)
- 2020: 11 (+219 -214)
- 2019: 82 (+1766 -1662)
- 2018: 67 (+863 -656).
- 2017: 1 (+1052 -1052).
Total : 185 (+4110 -3787).
Historique
- 2019-02-20 : Meilleure version de la commande pour trouver les erreurs du type
comparison between bytes and ...
. - 2019-02-13 : Ajout de la commande pour trouver les erreurs du type
comparison between bytes and ...
. - 2019-01-03 : Ajout de la commande pour trouver les erreurs du type
invalid escape sequence
. - 2019-01-01 : Ajout de la section statistiques.
- 2018-10-04 : Ajout de la function func3 utilisant le bloc
try...finally
; nouvelle version de la fixtureno_warnings()
.