diff --git a/long_test/deep_proxmox.py b/long_test/deep_proxmox.py index ea91dc6..4195bb9 100755 --- a/long_test/deep_proxmox.py +++ b/long_test/deep_proxmox.py @@ -39,16 +39,12 @@ RACINE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, RACINE) sys.path.insert(0, os.path.join(RACINE, "long_test")) -from script.proxmox import proxmox_deploy as pve # noqa: E402 - import descente # noqa: E402 from descente import ( # noqa: E402,F401 DELAIS, - mener, - _lance_une_descente, Famille, + _lance_une_descente, a_defaire, - alias_etage as _alias_etage, autre_descente, capacite_hote, cle_publique, @@ -58,10 +54,18 @@ from descente import ( # noqa: E402,F401 detruire_etage1, dire, identite_de, + mener, module_qemu, - nom_etage as _nom_etage, retirer_alias, ) +from descente import ( + alias_etage as _alias_etage, +) +from descente import ( + nom_etage as _nom_etage, +) + +from script.proxmox import proxmox_deploy as pve # noqa: E402 # L'image des étages imbriqués. Debian parce que install_proxmox.sh s'installe # SUR une Debian — Proxmox ne publie pas d'image cloud. diff --git a/long_test/deep_qemu.py b/long_test/deep_qemu.py index bb5eaf8..2be47e6 100755 --- a/long_test/deep_qemu.py +++ b/long_test/deep_qemu.py @@ -42,15 +42,11 @@ RACINE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, RACINE) sys.path.insert(0, os.path.join(RACINE, "long_test")) -from script.proxmox import nesting # noqa: E402 -from script.proxmox import proxmox_deploy as pve # noqa: E402 - import descente # noqa: E402 from descente import ( # noqa: E402,F401 DELAIS, Famille, a_defaire, - alias_etage as _alias_etage, autre_descente, capacite_hote, cle_publique, @@ -62,9 +58,17 @@ from descente import ( # noqa: E402,F401 identite_de, mener, module_qemu, - nom_etage as _nom_etage, retirer_alias, ) +from descente import ( + alias_etage as _alias_etage, +) +from descente import ( + nom_etage as _nom_etage, +) + +from script.proxmox import nesting # noqa: E402 +from script.proxmox import proxmox_deploy as pve # noqa: E402 # Une Debian nue : c'est elle qui recevra libvirt et qemu-kvm. DISTRO = "debian" diff --git a/long_test/descente.py b/long_test/descente.py index f51dcf9..400417d 100644 --- a/long_test/descente.py +++ b/long_test/descente.py @@ -756,8 +756,7 @@ class Descente: ) else: self.dire( - f" profondeur atteinte : {atteint}" - f" / {self.plan['demandee']}" + f" profondeur atteinte : {atteint} / {self.plan['demandee']}" ) # Deux causes très différentes rendaient le même « 5 / 10 » : la # machine trop petite pour dix, ou un étage tombé en route. La @@ -1099,7 +1098,8 @@ def a_defaire(rapport, nom_base=""): etages = [ e for e in (rapport.get("etages") or []) - if identite_de(e) and e.get("parent_alias") + if identite_de(e) + and e.get("parent_alias") # « cree » est le seul champ qui dise que la machine est à NOUS. Les # deux autres conditions ne protégeaient que par accident : elles # tenaient parce que rien ne décrivait un hôte emprunté. Depuis qu'une diff --git a/script/analyse/analyse_schema_size.py b/script/analyse/analyse_schema_size.py index 7c4037a..04dac78 100755 --- a/script/analyse/analyse_schema_size.py +++ b/script/analyse/analyse_schema_size.py @@ -396,8 +396,7 @@ def _table_block(lst_row, exact, source="database"): ) return lines lines = [ - f" {'table':<34}{'total':>10}{'heap':>10}{'index':>10}" - f"{t('rows'):>14}" + f" {'table':<34}{'total':>10}{'heap':>10}{'index':>10}{t('rows'):>14}" ] for row in lst_row: count = row["exact_rows"] if exact else row["est_rows"] diff --git a/script/code_generator/new_project.py b/script/code_generator/new_project.py index 46c88a2..0e06010 100755 --- a/script/code_generator/new_project.py +++ b/script/code_generator/new_project.py @@ -382,7 +382,7 @@ class ProjectManagement: os.system(cmd) if not self.keep_bd_alive: - cmd = "./odoo_bin.sh db --drop --database" f" {bd_name_demo}" + cmd = f"./odoo_bin.sh db --drop --database {bd_name_demo}" _logger.info(cmd) os.system(cmd) @@ -463,7 +463,7 @@ class ProjectManagement: os.system(cmd) if not self.keep_bd_alive: - cmd = "./odoo_bin.sh db --drop --database" f" {bd_name_template}" + cmd = f"./odoo_bin.sh db --drop --database {bd_name_template}" _logger.info(cmd) os.system(cmd) @@ -531,7 +531,7 @@ class ProjectManagement: os.system(cmd) if not self.keep_bd_alive: - cmd = "./odoo_bin.sh db --drop --database" f" {bd_name_generator}" + cmd = f"./odoo_bin.sh db --drop --database {bd_name_generator}" _logger.info(cmd) os.system(cmd) diff --git a/script/code_generator/search_class_model.py b/script/code_generator/search_class_model.py index 3d2cd68..b6c2000 100755 --- a/script/code_generator/search_class_model.py +++ b/script/code_generator/search_class_model.py @@ -152,7 +152,7 @@ def search_and_replace( # new_file_content = ( # f'{f_lines[:t_index_second_quote]}"{models_name}"{f_lines[t_index_third_quote + len(second_char):]}' # ) - new_file_content = f'{f_lines[:t_index_second_quote]}"{models_name}"{f_lines[t_index_third_quote + len(second_char):]}' + new_file_content = f'{f_lines[:t_index_second_quote]}"{models_name}"{f_lines[t_index_third_quote + len(second_char) :]}' return new_file_content diff --git a/script/code_generator/technical/transform_xml_data_website_page_to_controller.py b/script/code_generator/technical/transform_xml_data_website_page_to_controller.py index 25864f5..f029671 100644 --- a/script/code_generator/technical/transform_xml_data_website_page_to_controller.py +++ b/script/code_generator/technical/transform_xml_data_website_page_to_controller.py @@ -116,7 +116,7 @@ def main(): ) module_name = get_module_name(config.source) method_name = ( - f'get_{"_".join(url.replace("-", "_").strip("/").split("/"))}' + f"get_{'_'.join(url.replace('-', '_').strip('/').split('/'))}" ) # template = f""" diff --git a/script/code_generator/transform_python_to_code_writer.py b/script/code_generator/transform_python_to_code_writer.py index d18258c..75e643b 100755 --- a/script/code_generator/transform_python_to_code_writer.py +++ b/script/code_generator/transform_python_to_code_writer.py @@ -645,7 +645,9 @@ def add_line( "with" f" cw.indent({4 + nb_space if nb_space else ''}):" ) - with cw.indent(): + with ( + cw.indent() + ): cw.emit( f'cw.emit("{line}")' ) @@ -723,7 +725,9 @@ def add_line( f"with" f" cw.indent():" ) - with cw.indent(): + with ( + cw.indent() + ): if ( no_indent != init_no_intend @@ -808,7 +812,9 @@ def add_line( f"with" f" cw.indent():" ) - with cw.indent(): + with ( + cw.indent() + ): if ( nb_indent - 1 @@ -902,7 +908,9 @@ def add_line( f"with" f" cw.indent():" ) - with cw.indent(): + with ( + cw.indent() + ): if ( nb_indent - 1 @@ -1007,7 +1015,9 @@ def add_line( f"with" f" cw.indent():" ) - with cw.indent(): + with ( + cw.indent() + ): if ( nb_indent - 1 @@ -1123,7 +1133,9 @@ def add_line( f"with" f" cw.indent():" ) - with cw.indent(): + with ( + cw.indent() + ): if ( nb_indent - 1 diff --git a/script/data/external_file.py b/script/data/external_file.py index 2ca5636..8dbf382 100644 --- a/script/data/external_file.py +++ b/script/data/external_file.py @@ -19,7 +19,7 @@ Les formats en pur stdlib — CSV, JSON, XML, et la détection de macros qui n'est qu'un `zipfile.namelist()` — tournent sous l'interpréteur du CLI. Excel et Access exigent un venv dédié. Le module doit donc s'importer sous les deux : AUCUN import de bibliothèque tierce au niveau du module, chacun -vit dans la fonction qui en a besoin. +vit dans la fonction qui en a besoin. Le canal de sortie ------------------ @@ -57,8 +57,7 @@ ERREURS = { "format_inconnu": "Format not recognised: ", "illisible_ici": "Recognised format, unreadable here" " — re-save it as .xlsx.", - "protege": "Protected by a password, or not a workbook" - " — unreadable here.", + "protege": "Protected by a password, or not a workbook — unreadable here.", "vide": "Empty file.", "droits": "Not readable: check the permissions.", "pas_un_fichier": "Not an ordinary file.", @@ -66,12 +65,11 @@ ERREURS = { " nothing was written.", "repertoire_non_vide": "The destination directory exists" " and is not empty.", - "aucune_feuille": "The selection matches no sheet;" - " nothing was written.", + "aucune_feuille": "The selection matches no sheet; nothing was written.", "tout_exclu": "Nothing was anonymised: every region was excluded.", "rien_a_faire": "Nothing to anonymise in this file.", "place": "Not enough room to write.", - "conversion_impossible": "This target cannot hold the source's" " shape: ", + "conversion_impossible": "This target cannot hold the source's shape: ", "table_source": "The mapping table would overwrite the source" " or the copy; nothing was written.", "table_illisible": "The mapping table is unreadable," diff --git a/script/data/external_file_formats.py b/script/data/external_file_formats.py index 1deec99..82c3c6d 100644 --- a/script/data/external_file_formats.py +++ b/script/data/external_file_formats.py @@ -2864,8 +2864,7 @@ def _avertissements(rapport, options): hors = rapport.get("hors_cellules") or {} if rapport.get("format") == "xlsx": dits.append( - "Cached formula results are dropped;" - " the sheet recomputes on open." + "Cached formula results are dropped; the sheet recomputes on open." ) dits.append("Document properties were cleared on the copy.") if hors.get("croises") or hors.get("graphiques"): @@ -2884,8 +2883,7 @@ def _avertissements(rapport, options): ) if hors.get("liens_externes"): dits.append( - "External links were dropped; formulas that used them" - " show #REF!." + "External links were dropped; formulas that used them show #REF!." ) survivent = _noms_de_feuille_survivent(rapport, options) if hors.get("plages_nommees") and survivent: diff --git a/script/database/db_restore.py b/script/database/db_restore.py index 60c4300..7aeb97f 100755 --- a/script/database/db_restore.py +++ b/script/database/db_restore.py @@ -273,8 +273,7 @@ def restore_or_clone(config, arg_base, cache_database, lst_db_cache): ) else: _logger.info( - f"## Clone cache {cache_database} to database" - f" {config.database} ##" + f"## Clone cache {cache_database} to database {config.database} ##" ) arg = ( f"{arg_base} --clone --from_database" diff --git a/script/database/image_db.py b/script/database/image_db.py index a4fb1bc..8d4d878 100755 --- a/script/database/image_db.py +++ b/script/database/image_db.py @@ -307,7 +307,7 @@ def main(): if not base_image_name or base_image_name == image_name_to_generate: with_demo = dct_config_image.get("with_demo") # Create a new one - cmd = f"./odoo_bin.sh db --create --database" f" {bd_temp_name}" + cmd = f"./odoo_bin.sh db --create --database {bd_temp_name}" if with_demo: cmd += " --demo" else: diff --git a/script/database/list_remote.py b/script/database/list_remote.py index 9c1e22a..b4268c2 100755 --- a/script/database/list_remote.py +++ b/script/database/list_remote.py @@ -1,7 +1,8 @@ #!/usr/bin/env bash -import xmlrpc.client import sys +import xmlrpc.client + import click diff --git a/script/database/migrate/process_backup_file.py b/script/database/migrate/process_backup_file.py index 6ce8d87..71d3fba 100755 --- a/script/database/migrate/process_backup_file.py +++ b/script/database/migrate/process_backup_file.py @@ -12,15 +12,14 @@ def process_zip( ): # Ouvrir le zip d'entrée en lecture try: - with zipfile.ZipFile(path_backup_zip, "r") as zin, zipfile.ZipFile( - path_output_zip, "w" - ) as zout: - + with ( + zipfile.ZipFile(path_backup_zip, "r") as zin, + zipfile.ZipFile(path_output_zip, "w") as zout, + ): # Parcourir tous les fichiers du zip for item in zin.infolist(): data = zin.read(item.filename) if item.filename == file_to_modify: - try: # On suppose un fichier texte en UTF-8 text = data.decode("utf-8") diff --git a/script/docker/docker_update_version.py b/script/docker/docker_update_version.py index 866734a..41ffd75 100755 --- a/script/docker/docker_update_version.py +++ b/script/docker/docker_update_version.py @@ -88,7 +88,7 @@ def edit_text(config): key = "image:" value = lst_docker_info[i] lst_docker_info[i] = ( - f"{value[:value.find(key) + len(key)]} {config.prod_version}\n" + f"{value[: value.find(key) + len(key)]} {config.prod_version}\n" ) break if "ERPLibre" in docker_info: diff --git a/script/execute/execute.py b/script/execute/execute.py index e3ba8e7..01d4dc2 100644 --- a/script/execute/execute.py +++ b/script/execute/execute.py @@ -101,7 +101,7 @@ class Execute: f"gnome-terminal -- bash -c 'source" f" ./{VENV_ERPLIBRE}/bin/activate;%s'" ) - self.cmd_source_default = "gnome-terminal -- bash -c '" f"%s'" + self.cmd_source_default = f"gnome-terminal -- bash -c '%s'" else: exec_path_tell = shutil.which("osascript") if exec_path_tell: @@ -280,7 +280,7 @@ class Execute: process.wait() exit_code = process.returncode if process.returncode != 0 and not quiet: - print("Command returned error code:" f" {process.returncode}") + print(f"Command returned error code: {process.returncode}") # An exception MUST report a failure. exit_code stays None otherwise, # and None is falsy: callers testing « if not status: » would mark the diff --git a/script/git/commit_msg_lib.py b/script/git/commit_msg_lib.py index d449a63..6782f22 100644 --- a/script/git/commit_msg_lib.py +++ b/script/git/commit_msg_lib.py @@ -17,6 +17,7 @@ le rendra. Compté en CARACTÈRES et non en octets : « préchauffer » pèse 11 caractères et 12 octets, et une limite en octets refuserait des sujets français conformes. """ + import re import sys from pathlib import Path diff --git a/script/git/git_local_server.py b/script/git/git_local_server.py index 3f1803b..5890218 100755 --- a/script/git/git_local_server.py +++ b/script/git/git_local_server.py @@ -81,9 +81,7 @@ Use --production-ready for /srv/git (requires root). "-p", "--path", default=None, - help=( - "Path for git server bare repos" f" (default: {DEFAULT_GIT_PATH})" - ), + help=(f"Path for git server bare repos (default: {DEFAULT_GIT_PATH})"), ) parser.add_argument( "--production-ready", @@ -97,12 +95,12 @@ Use --production-ready for /srv/git (requires root). "-m", "--manifest", default=DEFAULT_MANIFEST, - help="Manifest XML file" f" (default: {DEFAULT_MANIFEST})", + help=f"Manifest XML file (default: {DEFAULT_MANIFEST})", ) parser.add_argument( "--remote-name", default=DEFAULT_REMOTE_NAME, - help="Remote name to add" f" (default: {DEFAULT_REMOTE_NAME})", + help=f"Remote name to add (default: {DEFAULT_REMOTE_NAME})", ) parser.add_argument( "--port", @@ -150,9 +148,7 @@ Use --production-ready for /srv/git (requires root). "--jobs", type=int, default=DEFAULT_JOBS, - help=( - "Parallel jobs for init/remote/push" f" (default: {DEFAULT_JOBS})" - ), + help=(f"Parallel jobs for init/remote/push (default: {DEFAULT_JOBS})"), ) parser.add_argument( "-v", @@ -367,8 +363,7 @@ async def _add_single_remote( ) if rc != 0: _logger.warning( - f" set-url failed for" - f" {project['path']}: {err.strip()}" + f" set-url failed for {project['path']}: {err.strip()}" ) return "error" _logger.info(f" Updated: {project['path']}") @@ -385,7 +380,7 @@ async def _add_single_remote( ) if rc != 0: _logger.warning( - f" add failed for" f" {project['path']}: {err.strip()}" + f" add failed for {project['path']}: {err.strip()}" ) return "error" _logger.info(f" Added: {project['path']}") @@ -428,7 +423,7 @@ async def add_remotes( added = results.count("added") updated = results.count("updated") errors = results.count("error") - print(f"Remotes: {added} added, {updated} updated," f" {errors} errors") + print(f"Remotes: {added} added, {updated} updated, {errors} errors") # --- Async workers for push --- @@ -568,7 +563,7 @@ async def _push_single_repo( "receive.shallowUpdate", "true", ) - _logger.info(f" Shallow push for" f" {project['path']}") + _logger.info(f" Shallow push for {project['path']}") # Handle detached HEAD: checkout manifest branch did_checkout = False @@ -611,9 +606,7 @@ async def _push_single_repo( _, err, rc = await _run_git(*cmd, timeout=120) if rc != 0: _logger.warning( - f" Push failed for" - f" {project['path']}:" - f" {err.strip()}" + f" Push failed for {project['path']}: {err.strip()}" ) return "error", did_checkout else: @@ -656,8 +649,7 @@ async def push_to_local( errors = sum(1 for s, _ in results if s == "error") checkouts = sum(1 for _, c in results if c) print( - f"Push: {pushed} pushed, {checkouts} branch" - f" checkouts, {errors} errors" + f"Push: {pushed} pushed, {checkouts} branch checkouts, {errors} errors" ) @@ -682,9 +674,7 @@ def print_clone_commands(git_path, projects, port): clone_path = project["path"] if clone_path == ".": clone_path = "erplibre" - lines.append( - f" git clone {base_url}/{repo_name}" f" {clone_path}" - ) + lines.append(f" git clone {base_url}/{repo_name} {clone_path}") lines.sort() for line in lines: print(line) diff --git a/script/git/git_tool.py b/script/git/git_tool.py index 446223a..1eac030 100644 --- a/script/git/git_tool.py +++ b/script/git/git_tool.py @@ -419,7 +419,7 @@ class GitTool: # '"${EL_CONFIG_FILE}"\n' # ) str_repo = ( - f' printf "{update_repo}," >> ' '"${EL_CONFIG_FILE}"\n' + f' printf "{update_repo}," >> "${{EL_CONFIG_FILE}}"\n' ) # Ignore repo if not starting by addons # if update_repo.startswith("addons"): diff --git a/script/git/github_api.py b/script/git/github_api.py index a806eae..161eef8 100644 --- a/script/git/github_api.py +++ b/script/git/github_api.py @@ -35,7 +35,7 @@ def get_pull_request_repo( user_name = user["login"] if not organization_name else organization_name status, lst_pull = gh.repos[user_name][parsed_url.repo].pulls.get() if type(lst_pull) is dict: - print(f"For url {upstream_url}," f" got {lst_pull.get('message')}") + print(f"For url {upstream_url}, got {lst_pull.get('message')}") return False else: for pull in lst_pull: @@ -106,9 +106,7 @@ def add_and_fetch_remote( except git.NoSuchPathError: print(f"New repo {repo_info.relative_path}") if not root_repo: - print( - "Missing git repository to root for repo" f" {repo_info.path}" - ) + print(f"Missing git repository to root for repo {repo_info.path}") return if branch_name: submodule_repo = retry( diff --git a/script/proxmox/proxmox_deploy.py b/script/proxmox/proxmox_deploy.py index f52a609..4fcdfb7 100644 --- a/script/proxmox/proxmox_deploy.py +++ b/script/proxmox/proxmox_deploy.py @@ -18,6 +18,7 @@ ce module. Découpage voulu : TOUT ce qui construit une commande ou lit une sortie est une fonction PURE, vérifiable sans hôte Proxmox. Seul `run()` parle au réseau. """ + from __future__ import annotations import ipaddress diff --git a/script/selenium/selenium_lib.py b/script/selenium/selenium_lib.py index 3b520c9..b06b93c 100644 --- a/script/selenium/selenium_lib.py +++ b/script/selenium/selenium_lib.py @@ -1116,8 +1116,10 @@ class SeleniumLib(object): self.driver.refresh() if wait_ready: WebDriverWait(self.driver, 15).until( - lambda d: d.execute_script("return document.readyState") - == "complete" + lambda d: ( + d.execute_script("return document.readyState") + == "complete" + ) ) def check_bot_chat_and_close(self): @@ -1824,10 +1826,13 @@ class SeleniumLib(object): ) ) WebDriverWait(driver, 10).until( - lambda d: picker.find_element( - By.CSS_SELECTOR, ".o_datetime_picker_header .o_header_part" - ).text - != header_txt + lambda d: ( + picker.find_element( + By.CSS_SELECTOR, + ".o_datetime_picker_header .o_header_part", + ).text + != header_txt + ) ) else: raise RuntimeError( diff --git a/script/test/run_parallel_test.py b/script/test/run_parallel_test.py index c89417a..9e62b3f 100755 --- a/script/test/run_parallel_test.py +++ b/script/test/run_parallel_test.py @@ -556,7 +556,7 @@ async def test_exec( new_hook_line = ( hook_line[: first_index + len(f_key)] + f'"{s_first_path}"\n' - + f'{nb_space_indentation * " "}template_dir =' + + f"{nb_space_indentation * ' '}template_dir =" f' "{s_first_path}/" + MODULE_NAME\n\n' + hook_line[index_end_string:] ) diff --git a/script/todo/assistant/backends.py b/script/todo/assistant/backends.py index a83884e..44f04fd 100644 --- a/script/todo/assistant/backends.py +++ b/script/todo/assistant/backends.py @@ -29,6 +29,7 @@ l'identique : un seul client `openai` les couvre toutes, pointé sur ce que `servers.base_url()` rend. C'est aussi ce client qu'un test injecte pour parler à un vrai serveur de boucle locale plutôt qu'à un double. """ + from __future__ import annotations import json diff --git a/script/todo/assistant/capabilities.py b/script/todo/assistant/capabilities.py index 862e3a6..a13fbd9 100644 --- a/script/todo/assistant/capabilities.py +++ b/script/todo/assistant/capabilities.py @@ -33,6 +33,7 @@ d'où une échelle qui teste `is_loopback` d'abord, `is_private` ensuite. Et ne résout pas se lit comme `global`, la lecture pessimiste, jamais comme satisfait. """ + from __future__ import annotations import ipaddress diff --git a/script/todo/assistant/chat.py b/script/todo/assistant/chat.py index d6021f3..b0924a0 100644 --- a/script/todo/assistant/chat.py +++ b/script/todo/assistant/chat.py @@ -25,6 +25,7 @@ Les valeurs de `COMMANDS` SONT les clés i18n : `t()` rend une clé absente inchangée, donc une commande non traduite s'affiche en anglais correct. La traduction se fait à l'affichage, dans le menu. """ + from __future__ import annotations import re diff --git a/script/todo/assistant/claude_sessions.py b/script/todo/assistant/claude_sessions.py index f659e15..fa0d654 100644 --- a/script/todo/assistant/claude_sessions.py +++ b/script/todo/assistant/claude_sessions.py @@ -35,6 +35,7 @@ deux dépôts voisins s'y confondent. machine se compte en dizaines de mégaoctets ; seules les premières lignes sont lues, et le listage se garde de les relire à chaque affichage. """ + from __future__ import annotations import json diff --git a/script/todo/assistant/context.py b/script/todo/assistant/context.py index 5805407..6c8c45d 100644 --- a/script/todo/assistant/context.py +++ b/script/todo/assistant/context.py @@ -35,6 +35,7 @@ d'ordinaire. La moitié « noms » du filtre est donc inerte par défaut, et `gate` le dit au lieu de laisser croire à un contrôle complet : une destination tierce est REFUSÉE tant que cette liste est vide. """ + from __future__ import annotations import os diff --git a/script/todo/assistant/discover.py b/script/todo/assistant/discover.py index 0db3413..65c2aa3 100644 --- a/script/todo/assistant/discover.py +++ b/script/todo/assistant/discover.py @@ -41,6 +41,7 @@ D'où l'ordre des passes : une passe de CONNEXION SEULE sur tout le réseau d'abord, puis le budget coûteux des GET de reconnaissance dépensé sur la poignée d'hôtes qui ont accepté. `sweep` fait la première, et rien d'autre. """ + from __future__ import annotations import ipaddress @@ -50,9 +51,8 @@ import shlex import socket import subprocess import time -from concurrent.futures import ThreadPoolExecutor +from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import TimeoutError as PoolTimeout -from concurrent.futures import as_completed from dataclasses import dataclass from script.todo.assistant import fingerprint diff --git a/script/todo/assistant/fingerprint.py b/script/todo/assistant/fingerprint.py index fa179de..e78edbc 100644 --- a/script/todo/assistant/fingerprint.py +++ b/script/todo/assistant/fingerprint.py @@ -31,6 +31,7 @@ un 503 « starting » ou « Loading model » est vivant et identifié, un 401 es un accord de reconnaissance et jamais une invitation à saisir une clé, et un corps tronqué vaut ce qui en est arrivé. """ + from __future__ import annotations import functools diff --git a/script/todo/assistant/gpt.py b/script/todo/assistant/gpt.py index 7f5999b..aafb0f1 100644 --- a/script/todo/assistant/gpt.py +++ b/script/todo/assistant/gpt.py @@ -38,6 +38,7 @@ d'où deux restrictions sur elle : `hosting` y est forcé à `loopback`, et un gpt qui y déclarerait une COMMANDE est refusé. Un fichier qu'on n'a pas relu n'est pas une donnée, c'est de la configuration exécutable. """ + from __future__ import annotations import os diff --git a/script/todo/assistant/servers.py b/script/todo/assistant/servers.py index fffd42f..681feaf 100644 --- a/script/todo/assistant/servers.py +++ b/script/todo/assistant/servers.py @@ -26,6 +26,7 @@ dernier contact, ni rapport de balayage, ni résultat négatif. La liste de qui a répondu parmi les 254 adresses d'un /24 décrit des machines que personne n'a désignées, là où un serveur retenu en désigne une seule, volontairement. """ + from __future__ import annotations from dataclasses import dataclass, replace diff --git a/script/todo/database_manager.py b/script/todo/database_manager.py index cee81cf..5a9fea1 100644 --- a/script/todo/database_manager.py +++ b/script/todo/database_manager.py @@ -235,8 +235,7 @@ class DatabaseManager: ) commande = ( - f"python3 ./script/database/db_duplicate.py" - f" -s {source} -d {cible}" + f"python3 ./script/database/db_duplicate.py -s {source} -d {cible}" ) if neutraliser: commande += " --neutralize" @@ -392,8 +391,7 @@ class DatabaseManager: with zipfile.ZipFile(default_output_path, "r") as zip_ref: manifest_file_1 = zip_ref.open("manifest.json") _logger.info( - f"Log file '{default_output_path}' is complete" - " and validated." + f"Log file '{default_output_path}' is complete and validated." ) except Exception as e: _logger.error(e) diff --git a/script/todo/mail/account_setup.py b/script/todo/mail/account_setup.py index 829b0a2..d055c92 100644 --- a/script/todo/mail/account_setup.py +++ b/script/todo/mail/account_setup.py @@ -10,6 +10,7 @@ de demander l'information, mais doivent appeler exactement le même code une fois qu'ils l'ont : sinon les deux copies dérivent. Ce module ne connaît ni `input`, ni Textual, ni aucune bibliothèque d'interface. """ + from __future__ import annotations import os diff --git a/script/todo/mail/accounts.py b/script/todo/mail/accounts.py index b535da7..71a4cd9 100644 --- a/script/todo/mail/accounts.py +++ b/script/todo/mail/accounts.py @@ -12,6 +12,7 @@ Les préréglages `gmail`, `outlook` et `icloud` supposent un MOT DE PASSE D'APPLICATION : l'authentification simple ne passe plus autrement chez ces fournisseurs. C'est la limite assumée de la phase 1 ; la phase 2 apporte OAuth. """ + from __future__ import annotations import json diff --git a/script/todo/mail/charset.py b/script/todo/mail/charset.py index 95a4a8e..0bef11f 100644 --- a/script/todo/mail/charset.py +++ b/script/todo/mail/charset.py @@ -16,6 +16,7 @@ Cette fonction a été réinventée quatre fois dans ce paquet un charset non fiable ne doit jamais faire tomber l'affichage ou la synchronisation d'un message entier. """ + from __future__ import annotations diff --git a/script/todo/mail/crypto.py b/script/todo/mail/crypto.py index 887dd7b..b06a105 100644 --- a/script/todo/mail/crypto.py +++ b/script/todo/mail/crypto.py @@ -11,6 +11,7 @@ que de rendre du charabia. clair b"P0" + donnees chiffre b"E1" + nonce(12) + AES-256-GCM(chiffre || tag) """ + from __future__ import annotations import os diff --git a/script/todo/mail/imap_sync.py b/script/todo/mail/imap_sync.py index 2c696b9..87da569 100644 --- a/script/todo/mail/imap_sync.py +++ b/script/todo/mail/imap_sync.py @@ -15,6 +15,7 @@ plus rien dire, et garder l'ancien cache produirait des messages faux. Les corps ne descendent JAMAIS pendant une passe : une boîte de 20 000 messages doit se synchroniser en secondes, pas en gigaoctets. """ + from __future__ import annotations import logging diff --git a/script/todo/mail/imap_transport.py b/script/todo/mail/imap_transport.py index 7207c1c..962d54c 100644 --- a/script/todo/mail/imap_transport.py +++ b/script/todo/mail/imap_transport.py @@ -12,6 +12,7 @@ sur un cas limite ou l'autre. `BODY.PEEK` et non `BODY` : lire un message dans le TUI ne doit pas le marquer lu sur le serveur à l'insu de l'utilisateur. """ + from __future__ import annotations import email diff --git a/script/todo/mail/menu.py b/script/todo/mail/menu.py index e1fb35f..c652442 100644 --- a/script/todo/mail/menu.py +++ b/script/todo/mail/menu.py @@ -7,6 +7,7 @@ Ce module est le SEUL point de contact entre le paquet `mail` et le CLI : `todo.py` importe `prompt_execute_mail` et rien d'autre. Le sens de la dépendance est volontaire — `mail` ne doit jamais importer `todo`. """ + from __future__ import annotations import getpass diff --git a/script/todo/mail/secrets.py b/script/todo/mail/secrets.py index 8d97828..3a382be 100644 --- a/script/todo/mail/secrets.py +++ b/script/todo/mail/secrets.py @@ -16,6 +16,7 @@ Référence de secret : ":" kdbx:ERPLibre/Mail/perso/cache-key -> ... entrée cache-key keyring:perso -> service "erplibre-mail", user perso """ + from __future__ import annotations import logging diff --git a/script/todo/mail/smtp_send.py b/script/todo/mail/smtp_send.py index 35bf524..727a6c8 100644 --- a/script/todo/mail/smtp_send.py +++ b/script/todo/mail/smtp_send.py @@ -11,6 +11,7 @@ d'être caché tout en restant destinataire d'enveloppe. `date` et `msgid` sont injectables pour que les tests soient déterministes ; en production on laisse la stdlib les produire. """ + from __future__ import annotations import mimetypes diff --git a/script/todo/mail/store.py b/script/todo/mail/store.py index eba5c74..08c5e83 100644 --- a/script/todo/mail/store.py +++ b/script/todo/mail/store.py @@ -13,6 +13,7 @@ des personnes — expéditeur, destinataires, sujet, extrait, Message-ID — est scellé. Le Message-ID a en plus un haché salé par la clé, pour qu'on puisse recoller les fils de discussion sans le lire. """ + from __future__ import annotations import base64 diff --git a/script/todo/mail/tui.py b/script/todo/mail/tui.py index 628da49..b41df22 100644 --- a/script/todo/mail/tui.py +++ b/script/todo/mail/tui.py @@ -11,6 +11,7 @@ Une session = un compte ouvert. Elle survit à une panne réseau : le cache s'ouvre d'abord, la connexion est tentée ensuite, et son échec ne fait que poser un drapeau `online = False`. Une boîte hors ligne reste lisible. """ + from __future__ import annotations import logging @@ -549,10 +550,9 @@ def deliver(session, msg, send_fn=None, connect_fn=None) -> str: n'annule rien. Le message est déjà parti ; le signaler comme un échec pousserait l'utilisateur à l'envoyer deux fois. """ - from script.todo.mail.smtp_send import SmtpError + from script.todo.mail.smtp_send import SmtpError, without_bcc from script.todo.mail.smtp_send import connect as smtp_connect from script.todo.mail.smtp_send import send as smtp_send_fn - from script.todo.mail.smtp_send import without_bcc if not session.online: raise SmtpError(t("mail_offline_cannot_send")) @@ -706,9 +706,8 @@ def run_tui( return from script.todo import todo_prefs - from script.todo.mail import account_setup + from script.todo.mail import account_setup, tui_text from script.todo.mail import accounts as mail_accounts - from script.todo.mail import tui_text from script.todo.mail.accounts import PRESETS from script.todo.mail.secrets import SecretStore diff --git a/script/todo/mail/tui_text.py b/script/todo/mail/tui_text.py index 07163eb..3c6c270 100644 --- a/script/todo/mail/tui_text.py +++ b/script/todo/mail/tui_text.py @@ -12,6 +12,7 @@ charset menteur, pièce jointe sans nom. Aucune de ces fonctions ne lève ; au pire elles rendent une chaîne vide. Un message illisible doit s'afficher mal, pas faire tomber la boîte de réception. """ + from __future__ import annotations import datetime diff --git a/script/todo/migration_form.py b/script/todo/migration_form.py index d320208..492c1c3 100644 --- a/script/todo/migration_form.py +++ b/script/todo/migration_form.py @@ -15,6 +15,7 @@ en progression. La décision est donc écrite une seule fois. `ctx` vient de `TodoUpgrade.resume_context()` : pure donnée, aucun accès à la base ni au disque depuis l'affichage. """ + from __future__ import annotations try: diff --git a/script/todo/migration_stats.py b/script/todo/migration_stats.py index 715d7cc..588ad9b 100644 --- a/script/todo/migration_stats.py +++ b/script/todo/migration_stats.py @@ -13,6 +13,7 @@ autre session sans risquer de la perturber. réimplémenter une seconde fois la lecture des clés « state_* », qui divergerait de l'écran de reprise. """ + from __future__ import annotations import datetime diff --git a/script/todo/migration_status_tui.py b/script/todo/migration_status_tui.py index 9c54888..948a68e 100644 --- a/script/todo/migration_status_tui.py +++ b/script/todo/migration_status_tui.py @@ -142,7 +142,7 @@ def pane_text(dct, row, colour=False, show_log=True): if scan["errors"]: lignes.append("") lignes.append( - f"── {t('errors in the log')} :" f" {status.severe_count(scan)} ──" + f"── {t('errors in the log')} : {status.severe_count(scan)} ──" ) for item in scan["errors"][:15]: lignes.append( diff --git a/script/todo/proxmox_deploy_form.py b/script/todo/proxmox_deploy_form.py index dcf9d62..472d453 100644 --- a/script/todo/proxmox_deploy_form.py +++ b/script/todo/proxmox_deploy_form.py @@ -23,6 +23,7 @@ Le formulaire ne touche à rien : il rend une spec. C'est l'appelant import os import re +from script.todo.deploy_form_extras import ExtrasMixin from script.todo.deploy_form_lib import ( CSS_BASE, FREE, @@ -40,7 +41,6 @@ from script.todo.deploy_form_lib import ( res_row_widgets, t, ) -from script.todo.deploy_form_extras import ExtrasMixin from script.todo.deploy_form_plan import PlanMixin, preview_screen # Aucun disque orphelin à craindre : les disques d'un Proxmox distant vivent @@ -833,9 +833,9 @@ def run_proxmox_form(ctx, run_app: bool = True): self.profile = choix sur_mesure = choix == "custom" for champ in RES_FIELDS: - self.query_one(RES_FIELDS[champ][0], Select).disabled = ( - not sur_mesure - ) + self.query_one( + RES_FIELDS[champ][0], Select + ).disabled = not sur_mesure if not sur_mesure: self._show_free(champ, False) # Un réglage commun reprend la main sur les VM non figées : diff --git a/script/todo/proxmox_menu.py b/script/todo/proxmox_menu.py index c52299e..a9d8489 100644 --- a/script/todo/proxmox_menu.py +++ b/script/todo/proxmox_menu.py @@ -1824,8 +1824,7 @@ class ProxmoxMenuMixin: return False if fini.returncode: print( - f" ⚠ {t('download cache bypass not set')}" - f" ({fini.returncode})" + f" ⚠ {t('download cache bypass not set')} ({fini.returncode})" ) return False self._pve_note( @@ -1889,8 +1888,7 @@ class ProxmoxMenuMixin: # certificate in certificate chain » — plus tard, dans la VM, # loin d'ici. C'est pourquoi il est nommé. cle = ( - "no trust store for this distribution, its downloads " - "will fail" + "no trust store for this distribution, its downloads will fail" ) self._pve_note(vm, f" ⚠ {t(cle)} : {vm.get('distro') or '?'}") return False @@ -2009,8 +2007,7 @@ class ProxmoxMenuMixin: groupes = list(getattr(mod, "GPU_GROUPS", ()) or ("render", "video")) gestes = [f"sudo groupadd -f {g}" for g in groupes] gestes.append( - f"sudo usermod -aG {','.join(groupes)}" - f" {shlex.quote(utilisateur)}" + f"sudo usermod -aG {','.join(groupes)} {shlex.quote(utilisateur)}" ) code, _o = self._pve_ssh(cible, " && ".join(gestes), timeout=60) if code: diff --git a/script/todo/qemu_deploy_form.py b/script/todo/qemu_deploy_form.py index 9ac7f99..ff2df31 100644 --- a/script/todo/qemu_deploy_form.py +++ b/script/todo/qemu_deploy_form.py @@ -18,6 +18,7 @@ Le formulaire ne lance AUCUNE commande privilégiée ni réseau : tout appel Textual. Les données coûteuses (domaines existants, branches distantes) sont préchargées par l'appelant et arrivent dans `ctx`. """ + from __future__ import annotations import os diff --git a/script/todo/qemu_hardware.py b/script/todo/qemu_hardware.py index 3d5ca13..4246a93 100644 --- a/script/todo/qemu_hardware.py +++ b/script/todo/qemu_hardware.py @@ -25,6 +25,7 @@ Deux pièges, appris sur l'hôte : - « --add-device --graphics type=egl-headless » n'est PAS idempotent : appelé deux fois, il pose deux affichages. D'où la lecture de l'état AVANT le plan. """ + from __future__ import annotations import xml.etree.ElementTree as ET diff --git a/script/todo/qemu_install_monitor.py b/script/todo/qemu_install_monitor.py index 270b1b8..f93d085 100644 --- a/script/todo/qemu_install_monitor.py +++ b/script/todo/qemu_install_monitor.py @@ -11,6 +11,7 @@ quitter le dashboard n'arrête rien, on peut le rouvrir pour ré-attacher. - launch_installs(...) : lance les process détachés + écrit un manifeste JSON. - run_monitor(manifest_path) : ouvre le dashboard Textual sur un manifeste. """ + from __future__ import annotations import asyncio @@ -3282,9 +3283,7 @@ def run_monitor(manifest_path: str, run_app: bool = True): return with self.suspend(): print(f"\n=== {title} — {vm['name']} ===") - os.system( - f"{vm_ssh_prefix(vm)} " f"{shlex.quote(cmd)} || true" - ) + os.system(f"{vm_ssh_prefix(vm)} {shlex.quote(cmd)} || true") input("\nEntrée pour revenir au suivi… ") def _run_update(self, vm, parts) -> None: diff --git a/script/todo/qemu_manage.py b/script/todo/qemu_manage.py index c768977..1b751cf 100644 --- a/script/todo/qemu_manage.py +++ b/script/todo/qemu_manage.py @@ -16,13 +16,14 @@ from script.todo import ssh_config, todo_install from script.todo.qemu_cache_menu import bypass_menage from script.todo.qemu_privilege import ( LIBVIRT_URI as URI, +) +from script.todo.qemu_privilege import ( sudo_prefix, system_path, virsh_argv, ) from script.todo.todo_i18n import t - # Les fichiers d'état de dnsmasq, un par réseau libvirt. Sur une installation # standard ils sont en 0644 dans un répertoire en 0755, donc lisibles sans # privilège — le « .conf » posé à côté est en 0600, et c'est lui qui donne @@ -1682,10 +1683,7 @@ class QemuManageMixin: # Arrêt gracieux trop long : proposer un arrêt forcé. if self._is_yes( input( - t( - "Graceful shutdown timed out. Force off (destroy)? " - "(y/N): " - ) + t("Graceful shutdown timed out. Force off (destroy)? (y/N): ") ) ): cmd = ( @@ -2029,10 +2027,7 @@ class QemuManageMixin: print(f"⚠ {t('Not enough free space for a full backup.')}") return self._is_yes( input( - t( - "Back up anyway, at the risk of filling the disk?" - " (y/N): " - ) + t("Back up anyway, at the risk of filling the disk? (y/N): ") ) ) @@ -2684,9 +2679,7 @@ class QemuManageMixin: if proteges: print(f"\n{t('Kept (still attached to a VM):')}") for size, path, porteur in sorted(proteges, key=lambda o: -o[0]): - print( - f" {self._human_size(size):>9} {path}" f" ← {porteur}" - ) + print(f" {self._human_size(size):>9} {path} ← {porteur}") if orphans: total = sum(o[0] for o in orphans) print(f"\n{t('Orphan files:')}") @@ -3171,9 +3164,8 @@ class QemuManageMixin: `timeout` : délai max PAR VM (borne l'attente d'une VM sans IP). Un BATTEMENT toutes les 30 s liste les VM encore en attente -> jamais de silence prolongé qui donne l'impression d'un blocage.""" - from concurrent.futures import ThreadPoolExecutor + from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import TimeoutError as _FTimeout - from concurrent.futures import as_completed labels = labels or {} print( diff --git a/script/todo/ssh_config.py b/script/todo/ssh_config.py index f5ecc52..e8d1646 100644 --- a/script/todo/ssh_config.py +++ b/script/todo/ssh_config.py @@ -16,6 +16,7 @@ ligne, rien de plus. Un alias déclaré dans un fichier inclus reste donc invisible aux trois lecteurs, alors même que `ssh -G` le résoudrait — la source est incomplète sans être fausse. """ + from __future__ import annotations import re diff --git a/script/todo/textual_setup.py b/script/todo/textual_setup.py index eae64b1..8b7dc73 100644 --- a/script/todo/textual_setup.py +++ b/script/todo/textual_setup.py @@ -14,6 +14,7 @@ veut-on l'installer maintenant ? Module à part, et non une méthode de `TODO` : `todo_upgrade` en a besoin aussi, et il est importé PAR `todo` — le mettre là créerait un cycle. """ + from __future__ import annotations import importlib diff --git a/script/todo/todo_prefs.py b/script/todo/todo_prefs.py index 6d78a40..05829d5 100644 --- a/script/todo/todo_prefs.py +++ b/script/todo/todo_prefs.py @@ -13,6 +13,7 @@ télémétrie de navigation) et non dans un fichier versionné. Tout est best-effort : une préférence illisible ou un disque plein ne doivent JAMAIS empêcher le CLI de démarrer. """ + from __future__ import annotations import json diff --git a/script/todo/todo_telemetry.py b/script/todo/todo_telemetry.py index 940b5d3..6276262 100644 --- a/script/todo/todo_telemetry.py +++ b/script/todo/todo_telemetry.py @@ -10,6 +10,7 @@ DIAGRAMME arborescent des fonctionnalités dans un TUI Textual, trié par usage. dédupe les ré-affichages consécutifs pour ne compter que les TRANSITIONS. - run_tui() : ouvre l'arbre de navigation (compteurs par menu). """ + from __future__ import annotations import ast diff --git a/script/todo/todo_upgrade.py b/script/todo/todo_upgrade.py index 8f33afa..f9aabca 100755 --- a/script/todo/todo_upgrade.py +++ b/script/todo/todo_upgrade.py @@ -13,7 +13,6 @@ import sys import zipfile from uuid import uuid4 - from script.todo import ( auto_ask, migration_status, @@ -1196,8 +1195,8 @@ class TodoUpgrade: target_addons_path = dct_module.get("target_addons_path") source_addons_path = dct_module.get("source_addons_path") module_name = dct_module.get("module_name") - source_version_odoo = f'{dct_module.get("source_version_odoo")}.0' - target_version_odoo = f'{dct_module.get("target_version_odoo")}.0' + source_version_odoo = f"{dct_module.get('source_version_odoo')}.0" + target_version_odoo = f"{dct_module.get('target_version_odoo')}.0" source_module_path_to_copy = dct_module.get("source_module_path") # Prepare git environment for target if target_addons_path not in lst_path_git_clone_migrate: @@ -1512,7 +1511,7 @@ class TodoUpgrade: for next_version in range_version: print( - f"https://oca.github.io/OpenUpgrade/coverage_analysis/modules{next_version*10}-{(next_version+1)*10}.html" + f"https://oca.github.io/OpenUpgrade/coverage_analysis/modules{next_version * 10}-{(next_version + 1) * 10}.html" ) # ⚠️ ℹ 💬 ❗ 🔷 ✨ 🟦 🔹 🔵 ⟳ ⧖ ⚙ ✔ ✅ ❌ ⏵ ⏸ ⏹ ◆ ◇ … ➤ ⚑ ★ ☆ ☰ ⬍ ⍟ ⊗ ⌘ ⏻ ⍰ @@ -2041,8 +2040,7 @@ class TodoUpgrade: print(f"✅ -> {t('Clone done for Odoo')}{next_version}") else: print( - f"✅ -> {t('Clone already done for Odoo')}" - f"{next_version}" + f"✅ -> {t('Clone already done for Odoo')}{next_version}" ) option_comment += 1 @@ -2144,8 +2142,7 @@ class TodoUpgrade: ) else: print( - f"✅ -> {t('Switch already done for Odoo')}" - f"{next_version}" + f"✅ -> {t('Switch already done for Odoo')}{next_version}" ) lst_state_4_module_migrate_code = self.dct_progression.get( @@ -2526,8 +2523,7 @@ class TodoUpgrade: self.write_config() print( - f"✅ -> {t('Module upgrade done for Odoo')}" - f"{next_version}" + f"✅ -> {t('Module upgrade done for Odoo')}{next_version}" ) else: print( @@ -4047,8 +4043,7 @@ class TodoUpgrade: ) if lst_detail: print( - f"✨ {t('Modules to uninstall before Odoo')}" - f"{next_version} :" + f"✨ {t('Modules to uninstall before Odoo')}{next_version} :" ) self.print_uninstall_reason(lst_detail) return list(dict.fromkeys(list(extra) + lst_file)) @@ -4340,9 +4335,7 @@ class TodoUpgrade: # Clone a project for next version # Get actual branch cmd_git_clone_migrate_source = ( - f"cd {source_addons_path} && " - f"git branch --show-current && " - f"cd ~-" + f"cd {source_addons_path} && git branch --show-current && cd ~-" ) status, cmd_executed, lst_output = self.todo_upgrade_execute( cmd_git_clone_migrate_source, @@ -4408,9 +4401,7 @@ class TodoUpgrade: # TODO check config if path is added # Get remote branch address cmd_remote_address = ( - f"cd {source_addons_path} " - f"&& git remote get-url {remote} " - f"&& cd ~-" + f"cd {source_addons_path} && git remote get-url {remote} && cd ~-" ) status, cmd_executed, lst_output = self.todo_upgrade_execute( cmd_remote_address, diff --git a/script/todo/transform_form.py b/script/todo/transform_form.py index e26767b..36d2559 100644 --- a/script/todo/transform_form.py +++ b/script/todo/transform_form.py @@ -25,6 +25,7 @@ attend `{}` supprimerait le repli textuel en silence. partir du JSON de `--report` déjà en main : aucune entrée-sortie et aucun sous-processus depuis l'affichage. """ + from __future__ import annotations try: @@ -256,9 +257,9 @@ def run_transform_form(ctx, run_app: bool = True): # TOUS les imports textual ICI : le CLI importe ce module pour ses # fonctions pures, et les libellés des BINDINGS ne doivent être # évalués qu'à l'appel, après le choix de la langue. + from rich.text import Text from textual.app import App, ComposeResult from textual.containers import Horizontal, Vertical - from rich.text import Text from textual.widgets import DataTable, Footer, Header, OptionList, Static from textual.widgets.option_list import Option diff --git a/script/todo/transform_menu.py b/script/todo/transform_menu.py index a616390..592eed4 100644 --- a/script/todo/transform_menu.py +++ b/script/todo/transform_menu.py @@ -473,8 +473,7 @@ class TransformMenuMixin: ) if trouve is None: manque = t( - "The selection matches no sheet;" - " nothing was written." + "The selection matches no sheet; nothing was written." ) print(f"❌ {manque} {demandee}") return None @@ -685,8 +684,7 @@ class TransformMenuMixin: ) if apercu.get("hors_portee"): print( - f" {apercu['hors_portee']}" - f" {t('cell(s) left out of scope')}" + f" {apercu['hors_portee']} {t('cell(s) left out of scope')}" ) ecartees = apercu.get("colonnes_ecartees") or [] if ecartees: @@ -788,8 +786,7 @@ class TransformMenuMixin: ) ) avis = t( - "The file name is not anonymised;" - " the default does not reuse it." + "The file name is not anonymised; the default does not reuse it." ) print(f" ⚠ {avis}") reponse = self._transform_ask( @@ -870,10 +867,7 @@ class TransformMenuMixin: print(f" {cible}") attendu = os.path.basename(existants[0]) reponse = input( - t( - "This file already exists. Type its name in full to" - " overwrite: " - ) + t("This file already exists. Type its name in full to overwrite: ") ).strip() if reponse != attendu: print(f"❌ {t('Name does not match, nothing was written.')}") @@ -997,8 +991,7 @@ class TransformMenuMixin: print(f" {t('Left intact')} : {detail}") if bilan.get("hors_portee"): print( - f" {bilan['hors_portee']}" - f" {t('cell(s) left out of scope')}" + f" {bilan['hors_portee']} {t('cell(s) left out of scope')}" ) hors = bilan.get("hors_cellules") or {} # `isinstance(True, int)` vaut True : sans exclure les booléens, le diff --git a/script/version/erplibre_state.py b/script/version/erplibre_state.py index 9c5c529..4c3a808 100644 --- a/script/version/erplibre_state.py +++ b/script/version/erplibre_state.py @@ -74,7 +74,9 @@ def set_version_installed( ) -> None: """Record that an Odoo version has been installed (or reinstalled).""" state = read_state() - entry = state["odoo_versions"].get(odoo_version, _deep_copy(_EMPTY_VERSION_ENTRY)) + entry = state["odoo_versions"].get( + odoo_version, _deep_copy(_EMPTY_VERSION_ENTRY) + ) entry["installed"] = True entry["extra"] = extra if python: @@ -94,7 +96,9 @@ def set_version_installed( def set_version_switched(odoo_version: str) -> None: """Record that the workspace was switched to an Odoo version.""" state = read_state() - entry = state["odoo_versions"].get(odoo_version, _deep_copy(_EMPTY_VERSION_ENTRY)) + entry = state["odoo_versions"].get( + odoo_version, _deep_copy(_EMPTY_VERSION_ENTRY) + ) entry["switched_at"] = str(date.today()) state["odoo_versions"][odoo_version] = entry state["current_odoo_version"] = odoo_version diff --git a/script/vpn/anyconnect_xml.py b/script/vpn/anyconnect_xml.py index ede186c..ee07fa2 100644 --- a/script/vpn/anyconnect_xml.py +++ b/script/vpn/anyconnect_xml.py @@ -32,6 +32,7 @@ fichiers vus dans le parc déclarent `xmlns="http://schemas.xmlsoap.org/encoding/"`, et un site peut en distribuer un sans. Les balises sont donc cherchées sur leur nom local. """ + from __future__ import annotations import re diff --git a/script/vpn/drivers/base.py b/script/vpn/drivers/base.py index a85fc1f..b0398b7 100644 --- a/script/vpn/drivers/base.py +++ b/script/vpn/drivers/base.py @@ -24,6 +24,7 @@ Où vivent les fichiers, pour tous les pilotes : sans sudo : `status` en a besoin, et il tourne dans un autre processus que `up`. """ + from __future__ import annotations import ipaddress diff --git a/script/vpn/drivers/l2tp_ipsec.py b/script/vpn/drivers/l2tp_ipsec.py index 6990337..10606ad 100644 --- a/script/vpn/drivers/l2tp_ipsec.py +++ b/script/vpn/drivers/l2tp_ipsec.py @@ -37,6 +37,7 @@ Où vivent les fichiers, et pourquoi : /etc/ipsec.conf, /etc/ipsec.secrets un bloc marqué, retiré au « down ». /etc/strongswan.d/erplibre-vpn.conf le réglage install_routes. """ + from __future__ import annotations import os diff --git a/script/vpn/drivers/openconnect.py b/script/vpn/drivers/openconnect.py index 0dffa4a..6aa14e2 100644 --- a/script/vpn/drivers/openconnect.py +++ b/script/vpn/drivers/openconnect.py @@ -109,6 +109,7 @@ toutes deux à un moment où l'humain peut agir : le menu l'annonce avant la saisie du secret, et le montage compare les longueurs si ce qui est déposé la dépasse. Zéro = aucune limite. """ + from __future__ import annotations import json diff --git a/script/vpn/drivers/openvpn.py b/script/vpn/drivers/openvpn.py index 4407fcb..46cc5eb 100644 --- a/script/vpn/drivers/openvpn.py +++ b/script/vpn/drivers/openvpn.py @@ -26,6 +26,7 @@ Le tunnel scindé se demande à OpenVPN par `--route-nopull` : ignorer les routes poussées, puis poser les nôtres. C'est un gros marteau — il ignore aussi le DNS poussé — et le pilote le dit quand il le prend. """ + from __future__ import annotations import os diff --git a/script/vpn/drivers/sshuttle.py b/script/vpn/drivers/sshuttle.py index 8182d30..686aebd 100644 --- a/script/vpn/drivers/sshuttle.py +++ b/script/vpn/drivers/sshuttle.py @@ -21,6 +21,7 @@ Deux différences qui changent le code, et pas seulement les commandes : ouvrir la session SSH par root, avec les clés de root — c'est-à-dire aucune. D'où `sudo=False`, et un fichier de pid dans le home plutôt que dans /run. """ + from __future__ import annotations import os diff --git a/script/vpn/drivers/wireguard.py b/script/vpn/drivers/wireguard.py index 00d1770..1955621 100644 --- a/script/vpn/drivers/wireguard.py +++ b/script/vpn/drivers/wireguard.py @@ -17,6 +17,7 @@ compris, en « tout le trafic », l'astuce de marquage (fwmark) qui garde l'endpoint joignable. On ne double donc PAS son travail : un `ip route` de plus ici entrerait en conflit avec le sien. """ + from __future__ import annotations import shlex diff --git a/script/vpn/presets.py b/script/vpn/presets.py index c484764..79ffa84 100644 --- a/script/vpn/presets.py +++ b/script/vpn/presets.py @@ -35,6 +35,7 @@ une liste d'erreurs que l'appelant AFFICHE. Un préréglage fautif rendrait autrement tous les autres inatteignables, et la panne se lirait « aucun préréglage » alors qu'il y en a dix. """ + from __future__ import annotations import json diff --git a/script/vpn/profiles.py b/script/vpn/profiles.py index ece6f7c..de61e06 100644 --- a/script/vpn/profiles.py +++ b/script/vpn/profiles.py @@ -19,6 +19,7 @@ Toute valeur est VALIDÉE avant d'être écrite : elle finira dans un fichier de configuration et dans une ligne de commande lancée par sudo. Un nom d'hôte avec une espace ou un point-virgule n'y arrivera pas. """ + from __future__ import annotations import json diff --git a/script/vpn/runner.py b/script/vpn/runner.py index 07b1388..3df7703 100644 --- a/script/vpn/runner.py +++ b/script/vpn/runner.py @@ -20,6 +20,7 @@ utilisateur de la machine, `/proc//environ` par le seul propriétaire du processus. Un mot de passe en argument est visible de tous pendant toute la durée de la commande. """ + from __future__ import annotations import shlex diff --git a/script/vpn/valid.py b/script/vpn/valid.py index dc05b8b..aac95fe 100644 --- a/script/vpn/valid.py +++ b/script/vpn/valid.py @@ -14,6 +14,7 @@ refusé ICI, pas découvert par `sh`. Chaque fonction NORMALISE en place (`profile[key]` reçoit la valeur propre) et lève `ProfileError` avec un message destiné à l'humain. """ + from __future__ import annotations import ipaddress diff --git a/script/vpn/vault.py b/script/vpn/vault.py index 0ff3326..f3e4bbd 100644 --- a/script/vpn/vault.py +++ b/script/vpn/vault.py @@ -22,6 +22,7 @@ tout seul : `ensure_vault` demande, et une réponse vide fait renoncer. Un outil qui crée silencieusement un fichier de mots de passe dans un répertoire qu'on n'a pas choisi est un outil qu'on n'ose plus lancer. """ + from __future__ import annotations import getpass diff --git a/script/vpn/vpn.py b/script/vpn/vpn.py index c570def..ef49e90 100755 --- a/script/vpn/vpn.py +++ b/script/vpn/vpn.py @@ -19,6 +19,7 @@ de l'utilisateur et son mot de passe maître est saisi par lui. Chaque étape privilégiée appelle `sudo` séparément, et `--dry-run` les montre toutes sans en exécuter aucune. """ + from __future__ import annotations import argparse diff --git a/test/llm_fake_server.py b/test/llm_fake_server.py index 5cfbe14..f38b13b 100644 --- a/test/llm_fake_server.py +++ b/test/llm_fake_server.py @@ -26,6 +26,7 @@ Les valeurs y sont inventées — versions, noms de modèles, empreintes de compilation. Un relevé pris sur une machine réelle figerait dans le dépôt le nom d'un modèle et d'un hôte que personne n'a choisi d'y mettre. """ + from __future__ import annotations import http.server @@ -163,8 +164,7 @@ FIXTURES: dict[str, dict[str, tuple[int, bytes]]] = { "open_webui": { "/api/config": ( 200, - b'{"name":"Open WebUI","version":"0.0.0",' - b'"deployment_id":"0000"}', + b'{"name":"Open WebUI","version":"0.0.0","deployment_id":"0000"}', ), "/api/version": (200, b'{"version":"0.0.0"}'), }, diff --git a/test/mail_sandbox.py b/test/mail_sandbox.py index 09d8e47..46ba337 100644 --- a/test/mail_sandbox.py +++ b/test/mail_sandbox.py @@ -40,6 +40,7 @@ Rien ici ne sort de la machine : on se lie à 127.0.0.1 sur le port 0 (l'OS choisit), jamais sur un port fixe qui entrerait en collision avec ce qui écoute déjà. Aucun trousseau, aucun `~/.erplibre`, aucun identifiant réel. """ + from __future__ import annotations import atexit diff --git a/test/test_assistant_capabilities.py b/test/test_assistant_capabilities.py index f0fc2fa..d0e7389 100644 --- a/test/test_assistant_capabilities.py +++ b/test/test_assistant_capabilities.py @@ -25,6 +25,7 @@ lisent aussi les tests de transport : deux tables se seraient contredites sans que rien ne le montre. `/api/show` n'y est pas et se définit ici, parce que la découverte n'émet que des GET et qu'elle n'a donc jamais eu à le connaître. """ + import ipaddress import os import sys diff --git a/test/test_assistant_claude_sessions.py b/test/test_assistant_claude_sessions.py index cb439d9..907f84d 100644 --- a/test/test_assistant_claude_sessions.py +++ b/test/test_assistant_claude_sessions.py @@ -27,6 +27,7 @@ Aucun test ne lance `claude`, ne lit le registre de la machine, ni n'ouvre une transcription réelle : le lanceur, le registre, l'état des processus et la lecture d'en-tête sont tous injectés. """ + from __future__ import annotations import json @@ -160,7 +161,7 @@ class LeListageDesVivantes(unittest.TestCase): def _live(self, agents=AGENTS, registre=None, demarrage=DEMARRAGE): return CS.live( run=lambda argv: agents, - read_registry=lambda: (REGISTRE if registre is None else registre), + read_registry=lambda: REGISTRE if registre is None else registre, read_stat=lambda pid: stat_avec(demarrage), ) diff --git a/test/test_assistant_context.py b/test/test_assistant_context.py index ba61c5b..3d3be27 100644 --- a/test/test_assistant_context.py +++ b/test/test_assistant_context.py @@ -22,6 +22,7 @@ valeurs identifiantes des cas sont INVENTÉES — une règle qui interdit de nommer ne se cite pas elle-même en clair, et un test fige pour toujours ce qu'il porte. """ + from __future__ import annotations import os diff --git a/test/test_assistant_conversation.py b/test/test_assistant_conversation.py index 1d599d5..5b7e45f 100644 --- a/test/test_assistant_conversation.py +++ b/test/test_assistant_conversation.py @@ -25,6 +25,7 @@ Rien ici n'ouvre de socket sortante, ne lance de vrai `claude`, ni ne lit la configuration de la machine : le serveur est lié à la boucle locale sur un port choisi par le système, et le lanceur de sous-processus est injecté. """ + import json import os import sys diff --git a/test/test_assistant_gpt_loader.py b/test/test_assistant_gpt_loader.py index 01e5dce..7b7a9cc 100644 --- a/test/test_assistant_gpt_loader.py +++ b/test/test_assistant_gpt_loader.py @@ -21,6 +21,7 @@ l'ordre d'écriture : un chargeur se juge sur ce qu'il refuse. Aucun test ne lit le disque de la machine : les racines sont des répertoires temporaires, et le répertoire personnel est détourné là où il compte. """ + from __future__ import annotations import os diff --git a/test/test_assistant_menu.py b/test/test_assistant_menu.py index b8b356c..6ad467b 100644 --- a/test/test_assistant_menu.py +++ b/test/test_assistant_menu.py @@ -18,6 +18,7 @@ vérifiée ici même — le paquet, lui, doit rester importable seul. `_menu_header()` enregistre une télémétrie dans `~/.erplibre` : tout test qui appelle une méthode de menu la neutralise, sinon il écrit pour de vrai. """ + from __future__ import annotations import ast @@ -97,10 +98,11 @@ class Cablage(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch.object(TODO, "prompt_assistant_llm") as mock_llm, patch( - "script.todo.mail.menu.prompt_execute_mail" - ) as mock_mail, patch("click.prompt", side_effect=["1", "0"]), patch( - "script.todo.todo_telemetry.record" + with ( + patch.object(TODO, "prompt_assistant_llm") as mock_llm, + patch("script.todo.mail.menu.prompt_execute_mail") as mock_mail, + patch("click.prompt", side_effect=["1", "0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_assistant() mock_llm.assert_called_once_with() @@ -110,10 +112,11 @@ class Cablage(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch.object(TODO, "prompt_assistant_llm") as mock_llm, patch( - "script.todo.mail.menu.prompt_execute_mail" - ) as mock_mail, patch("click.prompt", side_effect=["2", "0"]), patch( - "script.todo.todo_telemetry.record" + with ( + patch.object(TODO, "prompt_assistant_llm") as mock_llm, + patch("script.todo.mail.menu.prompt_execute_mail") as mock_mail, + patch("click.prompt", side_effect=["2", "0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_assistant() mock_mail.assert_called_once() @@ -128,12 +131,13 @@ class Cablage(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch( - "script.todo.assistant.fingerprint.collect", return_value={} - ), patch("script.todo.assistant.servers.load", return_value=[]), patch( - "click.prompt", side_effect=["0"] - ), patch( - "script.todo.todo_telemetry.record" + with ( + patch( + "script.todo.assistant.fingerprint.collect", return_value={} + ), + patch("script.todo.assistant.servers.load", return_value=[]), + patch("click.prompt", side_effect=["0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_assistant_llm() @@ -244,16 +248,20 @@ class Balayage(unittest.TestCase): todo = self._todo() vus = {} - with patch.object( - llm_disc, "local_networks", return_value=[] - ), patch.object(llm_disc, "run_ip", return_value=""), patch.object( - todo, "_qemu_host_addresses", staticmethod(lambda: set()) - ), patch.object( - todo, - "_llm_probe_and_keep", - lambda adresses, **kw: vus.update({"n": len(adresses), "kw": kw}), - ), patch( - "click.prompt", side_effect=["198.51.100.0/24", "o"] + with ( + patch.object(llm_disc, "local_networks", return_value=[]), + patch.object(llm_disc, "run_ip", return_value=""), + patch.object( + todo, "_qemu_host_addresses", staticmethod(lambda: set()) + ), + patch.object( + todo, + "_llm_probe_and_keep", + lambda adresses, **kw: vus.update( + {"n": len(adresses), "kw": kw} + ), + ), + patch("click.prompt", side_effect=["198.51.100.0/24", "o"]), ): todo._llm_search_cidr() self.assertEqual(vus.get("n"), 254) @@ -269,16 +277,19 @@ class Balayage(unittest.TestCase): todo = self._todo() vus = {} voisinage = "198.51.100.1 dev lien0 lladdr aa:bb:cc:dd:ee:01 REACHABLE" - with patch.object( - llm_disc, "run_ip", return_value=voisinage - ), patch.object( - todo, "_qemu_host_addresses", staticmethod(lambda: set()) - ), patch.object( - todo, - "_llm_probe_and_keep", - lambda adresses, **kw: vus.update({"n": len(adresses), "kw": kw}), - ), patch( - "click.prompt", side_effect=["o"] + with ( + patch.object(llm_disc, "run_ip", return_value=voisinage), + patch.object( + todo, "_qemu_host_addresses", staticmethod(lambda: set()) + ), + patch.object( + todo, + "_llm_probe_and_keep", + lambda adresses, **kw: vus.update( + {"n": len(adresses), "kw": kw} + ), + ), + patch("click.prompt", side_effect=["o"]), ): todo._llm_sweep_cidr("198.51.100.0/24") self.assertEqual(vus.get("n"), 254) @@ -290,16 +301,19 @@ class Balayage(unittest.TestCase): todo = self._todo() vus = {} voisinage = "198.51.100.1 dev lien0 lladdr aa:bb:cc:dd:ee:01 REACHABLE" - with patch.object( - llm_disc, "run_ip", return_value=voisinage - ), patch.object( - todo, "_qemu_host_addresses", staticmethod(lambda: set()) - ), patch.object( - todo, - "_llm_probe_and_keep", - lambda adresses, **kw: vus.update({"n": len(adresses), "kw": kw}), - ), patch( - "click.prompt", side_effect=["v"] + with ( + patch.object(llm_disc, "run_ip", return_value=voisinage), + patch.object( + todo, "_qemu_host_addresses", staticmethod(lambda: set()) + ), + patch.object( + todo, + "_llm_probe_and_keep", + lambda adresses, **kw: vus.update( + {"n": len(adresses), "kw": kw} + ), + ), + patch("click.prompt", side_effect=["v"]), ): todo._llm_sweep_cidr("198.51.100.0/24") self.assertEqual(vus.get("n"), 1) @@ -315,8 +329,9 @@ class Balayage(unittest.TestCase): todo = self._todo() sortie = io.StringIO() - with patch.object(llm_disc, "sweep", return_value=[]), redirect_stdout( - sortie + with ( + patch.object(llm_disc, "sweep", return_value=[]), + redirect_stdout(sortie), ): todo._llm_probe_and_keep( ["198.51.100.1"], cible="198.51.100.0/24", restreint=True @@ -332,8 +347,9 @@ class Balayage(unittest.TestCase): todo = self._todo() sortie = io.StringIO() - with patch.object(llm_disc, "sweep", return_value=[]), redirect_stdout( - sortie + with ( + patch.object(llm_disc, "sweep", return_value=[]), + redirect_stdout(sortie), ): todo._llm_probe_and_keep(["198.51.100.1"], cible="198.51.100.0/24") self.assertNotIn( @@ -345,12 +361,15 @@ class Balayage(unittest.TestCase): todo = self._todo() appels = [] - with patch.object( - todo, "_qemu_host_addresses", staticmethod(lambda: set()) - ), patch.object(llm_disc, "run_ip", return_value=""), patch.object( - todo, "_llm_probe_and_keep", lambda *a, **k: appels.append(a) - ), patch( - "click.prompt", side_effect=[] + with ( + patch.object( + todo, "_qemu_host_addresses", staticmethod(lambda: set()) + ), + patch.object(llm_disc, "run_ip", return_value=""), + patch.object( + todo, "_llm_probe_and_keep", lambda *a, **k: appels.append(a) + ), + patch("click.prompt", side_effect=[]), ): todo._llm_sweep_cidr("10.0.0.0/8") self.assertEqual(appels, []) @@ -445,14 +464,13 @@ class SessionsClaudeCode(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch.object( - TODO, "prompt_claude_sessions" - ) as mock_sessions, patch.object( - TODO, "prompt_execute_claude_plugins" - ) as mock_plugins, patch( - "click.prompt", side_effect=["6", "0"] - ), patch( - "script.todo.todo_telemetry.record" + with ( + patch.object(TODO, "prompt_claude_sessions") as mock_sessions, + patch.object( + TODO, "prompt_execute_claude_plugins" + ) as mock_plugins, + patch("click.prompt", side_effect=["6", "0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_execute_gpt_code() mock_sessions.assert_called_once_with() @@ -463,10 +481,12 @@ class SessionsClaudeCode(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch( - "script.todo.assistant.claude_sessions.fleet", return_value=[] - ), patch("click.prompt", side_effect=["0"]), patch( - "script.todo.todo_telemetry.record" + with ( + patch( + "script.todo.assistant.claude_sessions.fleet", return_value=[] + ), + patch("click.prompt", side_effect=["0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_claude_sessions() @@ -491,9 +511,11 @@ class SessionsClaudeCode(unittest.TestCase): todo = TODO() sortie = io.StringIO() - with patch("shutil.which", return_value="/usr/bin/claude"), patch( - "click.prompt", side_effect=["0"] - ), redirect_stdout(sortie): + with ( + patch("shutil.which", return_value="/usr/bin/claude"), + patch("click.prompt", side_effect=["0"]), + redirect_stdout(sortie), + ): todo._claude_reprendre([]) self.assertIn(t("No session on this machine."), sortie.getvalue()) diff --git a/test/test_assistant_sweep.py b/test/test_assistant_sweep.py index 88a1e14..a2e5c93 100644 --- a/test/test_assistant_sweep.py +++ b/test/test_assistant_sweep.py @@ -45,8 +45,10 @@ sys.path.append( os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) ) -from script.todo.assistant import discover # noqa: E402 -from script.todo.assistant import fingerprint # noqa: E402 +from script.todo.assistant import ( + discover, # noqa: E402 + fingerprint, # noqa: E402 +) # Deux réseaux des blocs réservés à la documentation : ils ne peuvent # désigner aucune machine réelle. diff --git a/test/test_check_comment_hygiene.py b/test/test_check_comment_hygiene.py index 84d5173..6d0953d 100644 --- a/test/test_check_comment_hygiene.py +++ b/test/test_check_comment_hygiene.py @@ -11,6 +11,7 @@ un chemin en gabarit doivent passer sans un mot. La part qu'aucun motif ne juge — « cette phrase énonce-t-elle un fait durable ou raconte-t-elle une journée » — n'est pas testée : elle n'est pas décidable. """ + import os import subprocess import sys diff --git a/test/test_config_file.py b/test/test_config_file.py index f94e14d..89cddd0 100644 --- a/test/test_config_file.py +++ b/test/test_config_file.py @@ -94,24 +94,32 @@ class TestGetConfig(unittest.TestCase): base_path = self._write_json( "base.json", {"instance": [{"name": "test"}]} ) - with patch("script.config.config_file.CONFIG_FILE", base_path), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - os.path.join(self.tmpdir, "nonexistent1.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - os.path.join(self.tmpdir, "nonexistent2.json"), + with ( + patch("script.config.config_file.CONFIG_FILE", base_path), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + os.path.join(self.tmpdir, "nonexistent1.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + os.path.join(self.tmpdir, "nonexistent2.json"), + ), ): result = self.cfg.get_config("instance") self.assertEqual(result, [{"name": "test"}]) def test_get_config_returns_none_for_missing_key(self): base_path = self._write_json("base.json", {"a": 1}) - with patch("script.config.config_file.CONFIG_FILE", base_path), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - os.path.join(self.tmpdir, "nonexistent1.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - os.path.join(self.tmpdir, "nonexistent2.json"), + with ( + patch("script.config.config_file.CONFIG_FILE", base_path), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + os.path.join(self.tmpdir, "nonexistent1.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + os.path.join(self.tmpdir, "nonexistent2.json"), + ), ): result = self.cfg.get_config("missing") self.assertIsNone(result) @@ -125,12 +133,16 @@ class TestGetConfig(unittest.TestCase): "override.json", {"instance": [{"name": "override"}]}, ) - with patch("script.config.config_file.CONFIG_FILE", base_path), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - override_path, - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - os.path.join(self.tmpdir, "nonexistent.json"), + with ( + patch("script.config.config_file.CONFIG_FILE", base_path), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + override_path, + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + os.path.join(self.tmpdir, "nonexistent.json"), + ), ): result = self.cfg.get_config("instance") # Lists with extend: base + override @@ -148,12 +160,16 @@ class TestGetConfig(unittest.TestCase): "private.json", {"data": {"key": "private_val"}}, ) - with patch("script.config.config_file.CONFIG_FILE", base_path), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - os.path.join(self.tmpdir, "nonexistent.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - private_path, + with ( + patch("script.config.config_file.CONFIG_FILE", base_path), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + os.path.join(self.tmpdir, "nonexistent.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + private_path, + ), ): result = self.cfg.get_config("data") self.assertEqual(result, {"key": "private_val"}) @@ -171,12 +187,16 @@ class TestGetConfig(unittest.TestCase): "private.json", {"items": [3], "meta": {"a": "private"}}, ) - with patch("script.config.config_file.CONFIG_FILE", base_path), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - override_path, - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - private_path, + with ( + patch("script.config.config_file.CONFIG_FILE", base_path), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + override_path, + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + private_path, + ), ): result_items = self.cfg.get_config("items") result_meta = self.cfg.get_config("meta") @@ -188,15 +208,19 @@ class TestGetConfig(unittest.TestCase): self.assertEqual(result_meta, {"a": "private", "b": "override"}) def test_no_config_files_exist(self): - with patch( - "script.config.config_file.CONFIG_FILE", - os.path.join(self.tmpdir, "nope1.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - os.path.join(self.tmpdir, "nope2.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", - os.path.join(self.tmpdir, "nope3.json"), + with ( + patch( + "script.config.config_file.CONFIG_FILE", + os.path.join(self.tmpdir, "nope1.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + os.path.join(self.tmpdir, "nope2.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_PRIVATE_FILE", + os.path.join(self.tmpdir, "nope3.json"), + ), ): result = self.cfg.get_config("anything") self.assertIsNone(result) @@ -284,12 +308,15 @@ class TestSetConfigValue(unittest.TestCase): def test_round_trips_through_get_config_value(self): self.cfg.set_config_value(["kdbx", "path"], "/round/trip.kdbx") - with patch( - "script.config.config_file.CONFIG_FILE", - os.path.join(self.tmp.name, "nonexistent_base.json"), - ), patch( - "script.config.config_file.CONFIG_OVERRIDE_FILE", - os.path.join(self.tmp.name, "nonexistent_override.json"), + with ( + patch( + "script.config.config_file.CONFIG_FILE", + os.path.join(self.tmp.name, "nonexistent_base.json"), + ), + patch( + "script.config.config_file.CONFIG_OVERRIDE_FILE", + os.path.join(self.tmp.name, "nonexistent_override.json"), + ), ): result = self.cfg.get_config_value(["kdbx", "path"]) self.assertEqual(result, "/round/trip.kdbx") diff --git a/test/test_database_cleanup.py b/test/test_database_cleanup.py index a97003e..44a058c 100755 --- a/test/test_database_cleanup.py +++ b/test/test_database_cleanup.py @@ -449,9 +449,8 @@ class TestTheSilenceThatLookedLikeAHang(unittest.TestCase): def test_the_relay_shows_the_elapsed_time(self): # Ce qui distingue « ça avance lentement » de « ça ne bouge plus ». import io - from contextlib import redirect_stdout - import time as _time + from contextlib import redirect_stdout echo = cleanup.make_echo(_time.monotonic() - 42) out = io.StringIO() diff --git a/test/test_dms_access_repair.py b/test/test_dms_access_repair.py index e5742e9..1e32c99 100644 --- a/test/test_dms_access_repair.py +++ b/test/test_dms_access_repair.py @@ -158,8 +158,8 @@ class TestTheVersionGuard(unittest.TestCase): self.vraie = database_cleanup.require_matching_version self.vrai_shell = database_cleanup.run_shell self.appels = [] - database_cleanup.run_shell = ( - lambda *a, **k: self.appels.append(a) or {} + database_cleanup.run_shell = lambda *a, **k: ( + self.appels.append(a) or {} ) def tearDown(self): diff --git a/test/test_docker_update_version.py b/test/test_docker_update_version.py index 5c597e2..edc1446 100644 --- a/test/test_docker_update_version.py +++ b/test/test_docker_update_version.py @@ -21,7 +21,7 @@ class TestEditText(unittest.TestCase): def test_updates_image_after_erplibre(self): path = self._write_compose( - "services:\n" " ERPLibre:\n" " image: old:1.0\n" " ports:\n" + "services:\n ERPLibre:\n image: old:1.0\n ports:\n" ) config = SimpleNamespace( docker_compose_file=path, diff --git a/test/test_fix_account_root.py b/test/test_fix_account_root.py index 498e50b..828add3 100644 --- a/test/test_fix_account_root.py +++ b/test/test_fix_account_root.py @@ -187,8 +187,7 @@ class TestWhatItRefusesToTouch(Base): self.assertEqual( "r", self.lire( - "SELECT relkind FROM pg_class" - " WHERE relname = 'account_root'" + "SELECT relkind FROM pg_class WHERE relname = 'account_root'" ), ) diff --git a/test/test_fix_view_type.py b/test/test_fix_view_type.py index 0fb1ec3..994ce3c 100644 --- a/test/test_fix_view_type.py +++ b/test/test_fix_view_type.py @@ -572,8 +572,8 @@ class TestTheToolIsCalledWithTheRightContract(unittest.TestCase): def test_nothing_to_fix_asks_nothing(self): demandes = [] - self.obj.ask = ( - lambda prompt, default="": demandes.append(prompt) or "y" + self.obj.ask = lambda prompt, default="": ( + demandes.append(prompt) or "y" ) self.obj.todo_upgrade_execute = lambda cmd, **kw: (0, cmd) with redirect_stdout(io.StringIO()): diff --git a/test/test_format_file_to_commit.py b/test/test_format_file_to_commit.py index db5f51c..94b5349 100644 --- a/test/test_format_file_to_commit.py +++ b/test/test_format_file_to_commit.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from script.maintenance.format_file_to_commit import ( execute_shell, diff --git a/test/test_git_commit_msg.py b/test/test_git_commit_msg.py index af5743e..62aca3c 100644 --- a/test/test_git_commit_msg.py +++ b/test/test_git_commit_msg.py @@ -15,6 +15,7 @@ Les messages sont traduits. Les assertions qui citent du texte fixent donc la langue à « fr » pour la durée du module : sinon elles dépendraient de EL_LANG, et un poste en anglais les ferait toutes échouer. """ + import os import subprocess import sys diff --git a/test/test_git_tool.py b/test/test_git_tool.py index c38c86c..0c8480e 100644 --- a/test/test_git_tool.py +++ b/test/test_git_tool.py @@ -203,9 +203,7 @@ class TestStrInsert(unittest.TestCase): class TestGetProjectConfig(unittest.TestCase): def test_reads_github_token(self): content = ( - "#!/bin/bash\n" - 'EL_GITHUB_TOKEN="my_token_123"\n' - 'OTHER_VAR="value"\n' + '#!/bin/bash\nEL_GITHUB_TOKEN="my_token_123"\nOTHER_VAR="value"\n' ) with tempfile.NamedTemporaryFile( mode="w", suffix=".sh", delete=False, dir="/tmp" diff --git a/test/test_install_swig_compat.py b/test/test_install_swig_compat.py index a6eb4d0..f8327ed 100644 --- a/test/test_install_swig_compat.py +++ b/test/test_install_swig_compat.py @@ -48,7 +48,9 @@ PyObject *sonde(long v) { return PyInt_FromLong(v); } def _drapeau(): """Le -D tel qu'il est écrit dans le script — source unique.""" - m = re.search(r"export CPPFLAGS=\"[^\"]*?(-DPyInt_FromLong[^\" ]*)\"", SOURCE) + m = re.search( + r"export CPPFLAGS=\"[^\"]*?(-DPyInt_FromLong[^\" ]*)\"", SOURCE + ) return m.group(1) if m else "" @@ -65,7 +67,14 @@ def _compile(source, drapeaux): fichier = Path(tmp, "sonde.cpp") fichier.write_text(source, encoding="utf-8") res = subprocess.run( - [_compilateur(), "-fsyntax-only", *drapeaux, "-I", inc, str(fichier)], + [ + _compilateur(), + "-fsyntax-only", + *drapeaux, + "-I", + inc, + str(fichier), + ], capture_output=True, text=True, timeout=120, @@ -81,13 +90,13 @@ class TestLeDrapeauDansLeScript(unittest.TestCase): """Posé après, il n'atteindrait rien : c'est « poetry install » qui lance les compilations.""" export = SOURCE.index("export CPPFLAGS=") - install = SOURCE.index('install --no-root ${POETRY_VERBOSE}') + install = SOURCE.index("install --no-root ${POETRY_VERBOSE}") self.assertLess(export, install) def test_it_appends_instead_of_replacing(self): """Écraser CPPFLAGS jetterait ce que l'appelant y avait mis — sur s390x et openSUSE, des chemins d'en-têtes y passent.""" - self.assertIn('${CPPFLAGS:+${CPPFLAGS} }', SOURCE) + self.assertIn("${CPPFLAGS:+${CPPFLAGS} }", SOURCE) def test_it_is_cppflags_and_not_cflags(self): """Le piège qui a fait échouer le premier correctif : le fichier fautif @@ -145,9 +154,7 @@ class TestLeDrapeauCompile(unittest.TestCase): """Un hôte dont le SWIG écrit encore l'alias verrait, sinon, un avertissement de redéfinition par fichier compilé. En -Werror, ce serait un échec.""" - ok, sortie = _compile( - SWIG_42 + APPEL_SWIG_43, ["-Werror", _drapeau()] - ) + ok, sortie = _compile(SWIG_42 + APPEL_SWIG_43, ["-Werror", _drapeau()]) if ok is None: self.skipTest(sortie) self.assertTrue(ok, sortie[-400:]) diff --git a/test/test_kdbx_manager.py b/test/test_kdbx_manager.py index 24f9ba5..a8141ab 100644 --- a/test/test_kdbx_manager.py +++ b/test/test_kdbx_manager.py @@ -11,6 +11,7 @@ la clé i18n brute, sans nommer ce qu'elle demandait. Un mot de passe refusé est le cas NORMAL de cette fonction : elle doit le dire, laisser recommencer, et laisser partir. """ + import os import tempfile import unittest @@ -40,13 +41,16 @@ class KdbxCase(unittest.TestCase): def _saisies(self, *reponses): """Renvoie (résultat, texte affiché) pour une suite de saisies.""" vues = [] - with patch("getpass.getpass", side_effect=list(reponses)), patch( - "builtins.print", - # `**k` : un bouchon de `print` doit accepter la signature de - # `print`. Sans lui, ajouter un `flush=True` dans le code - # testé faisait échouer six tests sur une différence qui n'a - # rien à voir avec ce qu'ils vérifient. - side_effect=lambda *a, **k: vues.append(" ".join(map(str, a))), + with ( + patch("getpass.getpass", side_effect=list(reponses)), + patch( + "builtins.print", + # `**k` : un bouchon de `print` doit accepter la signature de + # `print`. Sans lui, ajouter un `flush=True` dans le code + # testé faisait échouer six tests sur une différence qui n'a + # rien à voir avec ce qu'ils vérifient. + side_effect=lambda *a, **k: vues.append(" ".join(map(str, a))), + ), ): resultat = self._manager().get_kdbx() return resultat, "\n".join(vues) @@ -86,9 +90,13 @@ class TestGivingUpIsPossible(KdbxCase): def test_giving_up_asks_only_once(self): appels = [] - with patch( - "getpass.getpass", side_effect=lambda **k: appels.append(1) or "" - ), patch("builtins.print"): + with ( + patch( + "getpass.getpass", + side_effect=lambda **k: appels.append(1) or "", + ), + patch("builtins.print"), + ): self._manager().get_kdbx() self.assertEqual(len(appels), 1) diff --git a/test/test_mail_compose.py b/test/test_mail_compose.py index 9b28b0e..46e789b 100644 --- a/test/test_mail_compose.py +++ b/test/test_mail_compose.py @@ -530,8 +530,8 @@ class TestComposeScreenMounted(unittest.IsolatedAsyncioTestCase): app = await self._mounted_app(self._FakeIMAPTransport()) orig_connect, orig_send = smtp_send_mod.connect, smtp_send_mod.send - smtp_send_mod.connect = ( - lambda account, password: self._FakeSMTPTransport() + smtp_send_mod.connect = lambda account, password: ( + self._FakeSMTPTransport() ) def boom(account, msg, transport): @@ -568,8 +568,8 @@ class TestComposeScreenMounted(unittest.IsolatedAsyncioTestCase): imap_transport = self._FakeIMAPTransport() app = await self._mounted_app(imap_transport) orig_connect, orig_send = smtp_send_mod.connect, smtp_send_mod.send - smtp_send_mod.connect = ( - lambda account, password: self._FakeSMTPTransport() + smtp_send_mod.connect = lambda account, password: ( + self._FakeSMTPTransport() ) smtp_send_mod.send = lambda account, msg, transport: [ "dest@example.com" @@ -637,8 +637,8 @@ class TestComposeScreenMounted(unittest.IsolatedAsyncioTestCase): return ["dest@example.com"] orig_connect, orig_send = smtp_send_mod.connect, smtp_send_mod.send - smtp_send_mod.connect = ( - lambda account, password: self._FakeSMTPTransport() + smtp_send_mod.connect = lambda account, password: ( + self._FakeSMTPTransport() ) smtp_send_mod.send = capture_send try: @@ -803,9 +803,10 @@ class TestBrowseFilesButton(TestComposeScreenMounted): async with app.run_test() as pilot: await pilot.press("c") await pilot.pause() - with patch.object( - type(app), "suspend", fake_suspend - ), patch.object(browser_mod, "FileBrowser", FakeFileBrowser): + with ( + patch.object(type(app), "suspend", fake_suspend), + patch.object(browser_mod, "FileBrowser", FakeFileBrowser), + ): app.screen.query_one("#browse_files", Button).press() await pilot.pause() diff --git a/test/test_mail_live_server.py b/test/test_mail_live_server.py index 76df3b9..4b68762 100644 --- a/test/test_mail_live_server.py +++ b/test/test_mail_live_server.py @@ -17,6 +17,7 @@ tout le fichier se saute proprement. Pour les lancer volontairement : .venv.erplibre/bin/python -m unittest discover -s test \\ -p test_mail_live_server.py -v """ + import email.utils import threading import unittest diff --git a/test/test_mail_menu.py b/test/test_mail_menu.py index 554eb68..8891bb3 100644 --- a/test/test_mail_menu.py +++ b/test/test_mail_menu.py @@ -80,24 +80,24 @@ class TestAddAccountRollsBack(unittest.TestCase): vault = MagicMock() vault.available_backends.return_value = ["kdbx"] - with patch.object( - menu, "secret_store_for", return_value=vault - ), patch.object( - menu.mail_accounts, "save", side_effect=OSError("disque plein") - ), patch.object( - menu, "_load_accounts", return_value=[] - ), patch( - "builtins.input", - side_effect=[ - "perso", - "moi@x.ca", - "", - "4", - "imap.x.ca", - "smtp.x.ca", - ], - ), patch( - "getpass.getpass", return_value="hunter2" + with ( + patch.object(menu, "secret_store_for", return_value=vault), + patch.object( + menu.mail_accounts, "save", side_effect=OSError("disque plein") + ), + patch.object(menu, "_load_accounts", return_value=[]), + patch( + "builtins.input", + side_effect=[ + "perso", + "moi@x.ca", + "", + "4", + "imap.x.ca", + "smtp.x.ca", + ], + ), + patch("getpass.getpass", return_value="hunter2"), ): menu._add_account(MagicMock()) @@ -111,24 +111,24 @@ class TestAddAccountRollsBack(unittest.TestCase): vault = MagicMock() vault.available_backends.return_value = ["kdbx"] - with patch.object( - menu, "secret_store_for", return_value=vault - ), patch.object( - menu.mail_accounts, "save", side_effect=OSError("disque plein") - ), patch.object( - menu, "_load_accounts", return_value=[] - ), patch( - "builtins.input", - side_effect=[ - "perso", - "moi@x.ca", - "", - "4", - "imap.x.ca", - "smtp.x.ca", - ], - ), patch( - "getpass.getpass", return_value="hunter2" + with ( + patch.object(menu, "secret_store_for", return_value=vault), + patch.object( + menu.mail_accounts, "save", side_effect=OSError("disque plein") + ), + patch.object(menu, "_load_accounts", return_value=[]), + patch( + "builtins.input", + side_effect=[ + "perso", + "moi@x.ca", + "", + "4", + "imap.x.ca", + "smtp.x.ca", + ], + ), + patch("getpass.getpass", return_value="hunter2"), ): menu._add_account(MagicMock()) # ne doit pas lever @@ -144,9 +144,13 @@ class TestOpenTuiAllowsEmptyAccounts(unittest.TestCase): import script.todo.mail.menu as menu todo = MagicMock() - with patch.object(menu, "_load_accounts", return_value=[]), patch( - "script.todo.mail.tui.open_sessions", return_value=[] - ) as mock_open, patch("script.todo.mail.tui.run_tui") as mock_run: + with ( + patch.object(menu, "_load_accounts", return_value=[]), + patch( + "script.todo.mail.tui.open_sessions", return_value=[] + ) as mock_open, + patch("script.todo.mail.tui.run_tui") as mock_run, + ): menu._open_tui(todo) mock_open.assert_called_once() @@ -161,13 +165,12 @@ class TestOpenTuiAllowsEmptyAccounts(unittest.TestCase): todo = MagicMock() secrets = MagicMock() - with patch.object( - menu, "_load_accounts", return_value=[] - ), patch.object(menu, "secret_store_for", return_value=secrets), patch( - "script.todo.mail.tui.open_sessions", return_value=[] - ), patch( - "script.todo.mail.tui.run_tui" - ) as mock_run: + with ( + patch.object(menu, "_load_accounts", return_value=[]), + patch.object(menu, "secret_store_for", return_value=secrets), + patch("script.todo.mail.tui.open_sessions", return_value=[]), + patch("script.todo.mail.tui.run_tui") as mock_run, + ): menu._open_tui(todo) _, kwargs = mock_run.call_args @@ -205,13 +208,13 @@ class TestSyncNowSurfacesResync(unittest.TestCase): pass buf = io.StringIO() - with patch.object( - menu, "_load_accounts", return_value=[account] - ), patch( - "script.todo.mail.tui.open_sessions", - return_value=[FakeSession()], - ), redirect_stdout( - buf + with ( + patch.object(menu, "_load_accounts", return_value=[account]), + patch( + "script.todo.mail.tui.open_sessions", + return_value=[FakeSession()], + ), + redirect_stdout(buf), ): menu._sync_now(MagicMock()) @@ -369,10 +372,10 @@ class TestCacheSizeAndPurge(unittest.TestCase): ref, value ) - with patch.object( - menu, "_load_accounts", return_value=[account] - ), patch.object(menu, "secret_store_for", return_value=secrets), patch( - "builtins.input", side_effect=["1", "o"] + with ( + patch.object(menu, "_load_accounts", return_value=[account]), + patch.object(menu, "secret_store_for", return_value=secrets), + patch("builtins.input", side_effect=["1", "o"]), ): menu._cache_size_and_purge(MagicMock()) # ne doit pas lever @@ -386,12 +389,10 @@ class TestCacheSizeAndPurge(unittest.TestCase): store.root.mkdir(parents=True, exist_ok=True) (store.root / "cache.db").write_bytes(b"pas une base sqlite" * 50) - with patch.object( - menu, "_load_accounts", return_value=[account] - ), patch.object( - menu, "secret_store_for", return_value=MagicMock() - ), patch( - "builtins.input", side_effect=["1", "o"] + with ( + patch.object(menu, "_load_accounts", return_value=[account]), + patch.object(menu, "secret_store_for", return_value=MagicMock()), + patch("builtins.input", side_effect=["1", "o"]), ): menu._cache_size_and_purge(MagicMock()) # ne doit pas lever @@ -469,9 +470,10 @@ class TestEnsureKdbx(unittest.TestCase): import script.todo.mail.menu as menu kdbx_path = os.path.join(self.tmp.name, "new.kdbx") - with mock_patch( - "builtins.input", side_effect=["1", kdbx_path] - ), mock_patch("getpass.getpass", side_effect=["hunter2", "hunter2"]): + with ( + mock_patch("builtins.input", side_effect=["1", kdbx_path]), + mock_patch("getpass.getpass", side_effect=["hunter2", "hunter2"]), + ): result = menu._ensure_kdbx(self.todo) self.assertTrue(result) self.assertTrue(os.path.isfile(kdbx_path)) @@ -486,10 +488,11 @@ class TestEnsureKdbx(unittest.TestCase): import script.todo.mail.menu as menu kdbx_path = os.path.join(self.tmp.name, "new.kdbx") - with mock_patch( - "builtins.input", side_effect=["1", kdbx_path] - ), mock_patch( - "getpass.getpass", side_effect=["hunter2", "somethingelse"] + with ( + mock_patch("builtins.input", side_effect=["1", kdbx_path]), + mock_patch( + "getpass.getpass", side_effect=["hunter2", "somethingelse"] + ), ): result = menu._ensure_kdbx(self.todo) self.assertFalse(result) @@ -537,9 +540,10 @@ class TestEnsureKdbx(unittest.TestCase): import script.todo.mail.menu as menu - with mock_patch.object( - menu.mail_accounts, "save" - ) as mock_save, mock_patch("builtins.input", side_effect=["0"]): + with ( + mock_patch.object(menu.mail_accounts, "save") as mock_save, + mock_patch("builtins.input", side_effect=["0"]), + ): menu._add_account(self.todo) mock_save.assert_not_called() # Le squelette du vrai `todo.json` donne "" (pas None) tant que @@ -609,14 +613,11 @@ class TestTodoWiring(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch.object( - TODO, "prompt_assistant_llm" - ) as mock_question, patch( - "script.todo.mail.menu.prompt_execute_mail" - ) as mock_mail, patch( - "click.prompt", side_effect=["1", "0"] - ), patch( - "script.todo.todo_telemetry.record" + with ( + patch.object(TODO, "prompt_assistant_llm") as mock_question, + patch("script.todo.mail.menu.prompt_execute_mail") as mock_mail, + patch("click.prompt", side_effect=["1", "0"]), + patch("script.todo.todo_telemetry.record"), ): todo.prompt_assistant() @@ -631,14 +632,11 @@ class TestTodoWiring(unittest.TestCase): from script.todo.todo import TODO todo = TODO() - with patch.object( - TODO, "prompt_assistant_llm" - ) as mock_question, patch( - "script.todo.mail.menu.prompt_execute_mail" - ) as mock_mail, patch( - "script.todo.todo_telemetry.record" - ), patch( - "click.prompt", side_effect=["2", "0"] + with ( + patch.object(TODO, "prompt_assistant_llm") as mock_question, + patch("script.todo.mail.menu.prompt_execute_mail") as mock_mail, + patch("script.todo.todo_telemetry.record"), + patch("click.prompt", side_effect=["2", "0"]), ): todo.prompt_assistant() @@ -674,10 +672,13 @@ class TestRetryPassword(unittest.TestCase): from script.todo.mail.menu import retry_password vues = [] - with patch( - "getpass.getpass", side_effect=lambda p="": vues.append(p) or "" - ), patch("script.todo.mail.menu.secret_store_for"), patch( - "builtins.print" + with ( + patch( + "getpass.getpass", + side_effect=lambda p="": vues.append(p) or "", + ), + patch("script.todo.mail.menu.secret_store_for"), + patch("builtins.print"), ): retry_password( self._todo(), @@ -742,11 +743,13 @@ class TestRetryPassword(unittest.TestCase): compte = account_from_preset("essai", "a@x.ca", preset_key) vues = [] - with patch("getpass.getpass", return_value=""), patch( - "script.todo.mail.menu.secret_store_for" - ), patch( - "builtins.print", - side_effect=lambda *a: vues.append(" ".join(map(str, a))), + with ( + patch("getpass.getpass", return_value=""), + patch("script.todo.mail.menu.secret_store_for"), + patch( + "builtins.print", + side_effect=lambda *a: vues.append(" ".join(map(str, a))), + ), ): retry_password( self._todo(), @@ -782,9 +785,10 @@ class TestRetryPassword(unittest.TestCase): def logout(self): pass - with patch("getpass.getpass", return_value="bon"), patch( - "script.todo.mail.menu.secret_store_for" - ) as store: + with ( + patch("getpass.getpass", return_value="bon"), + patch("script.todo.mail.menu.secret_store_for") as store, + ): store.return_value.set.side_effect = self.vault.__setitem__ ok = retry_password( self._todo(), @@ -802,9 +806,10 @@ class TestRetryPassword(unittest.TestCase): def refuse(account, password): raise OSError("530 refus") - with patch("getpass.getpass", return_value="faux"), patch( - "script.todo.mail.menu.secret_store_for" - ) as store: + with ( + patch("getpass.getpass", return_value="faux"), + patch("script.todo.mail.menu.secret_store_for") as store, + ): store.return_value.set.side_effect = self.vault.__setitem__ ok = retry_password( self._todo(), self.account, attempts=2, connect_fn=refuse diff --git a/test/test_mail_secrets.py b/test/test_mail_secrets.py index cadd2d0..90c89a3 100644 --- a/test/test_mail_secrets.py +++ b/test/test_mail_secrets.py @@ -182,13 +182,18 @@ class TestKeyringBranch(unittest.TestCase): def test_set_and_get_through_keyring(self): vault = {} - with patch( - "script.todo.mail.secrets.keyring_is_safe", return_value=True - ), patch( - "keyring.set_password", - side_effect=lambda s, u, p: vault.__setitem__((s, u), p), - ), patch( - "keyring.get_password", side_effect=lambda s, u: vault.get((s, u)) + with ( + patch( + "script.todo.mail.secrets.keyring_is_safe", return_value=True + ), + patch( + "keyring.set_password", + side_effect=lambda s, u, p: vault.__setitem__((s, u), p), + ), + patch( + "keyring.get_password", + side_effect=lambda s, u: vault.get((s, u)), + ), ): self.store.set("keyring:perso", "hunter2") self.assertEqual(self.store.get("keyring:perso"), "hunter2") @@ -196,9 +201,12 @@ class TestKeyringBranch(unittest.TestCase): def test_refuses_unsafe_backend(self): # `keyring.get_keyring` est patché AUSSI : le message d'erreur passe par # keyring_backend_name(), qui interrogerait sinon le vrai trousseau. - with patch( - "script.todo.mail.secrets.keyring_is_safe", return_value=False - ), patch("keyring.get_keyring", return_value=MagicMock()): + with ( + patch( + "script.todo.mail.secrets.keyring_is_safe", return_value=False + ), + patch("keyring.get_keyring", return_value=MagicMock()), + ): with self.assertRaises(SecretError) as ctx: self.store.set("keyring:perso", "hunter2") # Traduit : on compare à la clé i18n elle-même, pas au mot français, diff --git a/test/test_mail_sync.py b/test/test_mail_sync.py index 168113c..7bbbaf5 100644 --- a/test/test_mail_sync.py +++ b/test/test_mail_sync.py @@ -237,8 +237,8 @@ class TestNoselectContainers(SyncCase): transport = FakeImapTransport() transport.add("[Gmail]/Sent Mail", 1, subject="Envoyé") reels = transport.list_folders() - transport.list_folders = ( - lambda: [FolderInfo(name="[Gmail]", selectable=False)] + reels + transport.list_folders = lambda: ( + [FolderInfo(name="[Gmail]", selectable=False)] + reels ) # Le serveur RÉPONDRAIT NO : si le moteur tente quand même, le # test doit le voir échouer, pas passer par chance. diff --git a/test/test_mail_tui_account.py b/test/test_mail_tui_account.py index 4b59d29..7bc6f6c 100644 --- a/test/test_mail_tui_account.py +++ b/test/test_mail_tui_account.py @@ -10,6 +10,7 @@ Comme `test_mail_compose.py` : `on_mount` lit `todo_prefs`, qui crée `~/.erplibre/mail/accounts.json` par les mêmes fonctions que le CLI. `$HOME` est donc détourné vers un dossier jetable pour tout le module. """ + import os import tempfile import unittest @@ -352,9 +353,9 @@ class TestVaultScreenFirst(TuiAccountCase): vault_path = os.path.join(self.cache_dir.name, "new.kdbx") app.screen.query_one("#vault_path", Input).value = vault_path app.screen.query_one("#vault_password", Input).value = "hunter2" - app.screen.query_one("#vault_password_confirm", Input).value = ( - "hunter2" - ) + app.screen.query_one( + "#vault_password_confirm", Input + ).value = "hunter2" await pilot.click("#vault_create") await pilot.pause() @@ -380,9 +381,9 @@ class TestVaultScreenFirst(TuiAccountCase): vault_path = os.path.join(self.cache_dir.name, "new.kdbx") app.screen.query_one("#vault_path", Input).value = vault_path app.screen.query_one("#vault_password", Input).value = "hunter2" - app.screen.query_one("#vault_password_confirm", Input).value = ( - "autrechose" - ) + app.screen.query_one( + "#vault_password_confirm", Input + ).value = "autrechose" await pilot.click("#vault_create") await pilot.pause() @@ -421,9 +422,9 @@ class TestVaultScreenSurvivesDiskErrors(TuiAccountCase): vault_path = os.path.join(self.cache_dir.name, "new.kdbx") app.screen.query_one("#vault_path", Input).value = vault_path app.screen.query_one("#vault_password", Input).value = "hunter2" - app.screen.query_one("#vault_password_confirm", Input).value = ( - "hunter2" - ) + app.screen.query_one( + "#vault_password_confirm", Input + ).value = "hunter2" with patch( "script.todo.mail.account_setup.create_vault", @@ -459,9 +460,9 @@ class TestVaultScreenSurvivesDiskErrors(TuiAccountCase): vault_path = os.path.join(self.cache_dir.name, "new.kdbx") app.screen.query_one("#vault_path", Input).value = vault_path app.screen.query_one("#vault_password", Input).value = "hunter2" - app.screen.query_one("#vault_password_confirm", Input).value = ( - "hunter2" - ) + app.screen.query_one( + "#vault_password_confirm", Input + ).value = "hunter2" # `create_vault` (donc `create_kdbx`) tourne pour de vrai — le # fichier existe. Seule l'OUVERTURE qui suit échoue. @@ -811,9 +812,9 @@ class TestPasswordClearedBeforeDismiss(TuiAccountCase): vault_path = os.path.join(self.cache_dir.name, "new.kdbx") screen.query_one("#vault_path", Input).value = vault_path screen.query_one("#vault_password", Input).value = "hunter2" - screen.query_one("#vault_password_confirm", Input).value = ( - "hunter2" - ) + screen.query_one( + "#vault_password_confirm", Input + ).value = "hunter2" orig_dismiss = type(screen).dismiss diff --git a/test/test_mail_tui_help.py b/test/test_mail_tui_help.py index e751b2e..9dce807 100644 --- a/test/test_mail_tui_help.py +++ b/test/test_mail_tui_help.py @@ -15,6 +15,7 @@ Comme `test_mail_tui_splitter.py` : ce qui compte se mesure sur l'application montée pour de vrai, et sur ce qui est RÉELLEMENT rendu (`Compositor.render_strips`), jamais sur un attribut interne de l'écran. """ + import os import re import tempfile diff --git a/test/test_mail_tui_layout.py b/test/test_mail_tui_layout.py index aba6235..16bc9c3 100644 --- a/test/test_mail_tui_layout.py +++ b/test/test_mail_tui_layout.py @@ -11,6 +11,7 @@ Le reste — la classe CSS réellement posée sur `#panes`, la persistance dans — n'a de sens que sur l'application montée pour de vrai, comme `test_mail_tui_refresh.py`. """ + import os import tempfile import unittest diff --git a/test/test_mail_tui_log.py b/test/test_mail_tui_log.py index bc2c6c2..ec98119 100644 --- a/test/test_mail_tui_log.py +++ b/test/test_mail_tui_log.py @@ -10,6 +10,7 @@ reproduit exactement la plainte qui justifie son existence (« j'ai une erreur, mais aucun log »). Chaque état — absent, vide, illisible, aucune erreur de session — doit se dire en toutes lettres. """ + import os import tempfile import unittest diff --git a/test/test_mail_tui_refresh.py b/test/test_mail_tui_refresh.py index b776359..aeebe08 100644 --- a/test/test_mail_tui_refresh.py +++ b/test/test_mail_tui_refresh.py @@ -12,6 +12,7 @@ ci-dessous fait tourner le VRAI chemin (`_sync`, ou l'écran de composition via `ctrl+s`) plutôt que d'appeler `refresh_current_folder()` directement : un test qui ne franchit pas ce seuil ne prouverait rien sur le bug observé. """ + import os import tempfile import unittest @@ -347,8 +348,8 @@ class TestSendRefreshesTheOpenFolder(RefreshCase): app = await self._mounted_app(sessions=[session]) orig_connect, orig_send = smtp_send_mod.connect, smtp_send_mod.send - smtp_send_mod.connect = ( - lambda account, password: self._FakeSMTPTransport() + smtp_send_mod.connect = lambda account, password: ( + self._FakeSMTPTransport() ) smtp_send_mod.send = lambda account, msg, transport: [ "dest@example.com" diff --git a/test/test_mail_tui_resize.py b/test/test_mail_tui_resize.py index eef4704..e2bbc19 100644 --- a/test/test_mail_tui_resize.py +++ b/test/test_mail_tui_resize.py @@ -9,6 +9,7 @@ réellement posé sur les widgets, la persistance, la non-perturbation des autres dispositions — n'a de sens que sur l'application montée pour de vrai. """ + import os import tempfile import unittest diff --git a/test/test_mail_tui_splitter.py b/test/test_mail_tui_splitter.py index d3e6089..f9554a5 100644 --- a/test/test_mail_tui_splitter.py +++ b/test/test_mail_tui_splitter.py @@ -15,6 +15,7 @@ coordonnée ÉCRAN absolue (`widget=None`), pas la barre elle-même, parce que la barre se déplace pendant le glissement (son voisin redimensionné la pousse) — cibler à nouveau la barre par sélecteur dériverait. """ + import os import tempfile import unittest diff --git a/test/test_mail_tui_text.py b/test/test_mail_tui_text.py index f6e7087..5565c24 100644 --- a/test/test_mail_tui_text.py +++ b/test/test_mail_tui_text.py @@ -126,10 +126,7 @@ class TestExtractBody(unittest.TestCase): def test_survives_unknown_8bit(self): """Étiquette réelle observée en usage, pas seulement un charset inventé (voir `script/todo/mail/charset.py`).""" - raw = ( - b"Content-Type: text/plain; charset=unknown-8bit\r\n\r\n" - b"Bonjour" - ) + raw = b"Content-Type: text/plain; charset=unknown-8bit\r\n\r\nBonjour" text, _ = extract_body(raw) self.assertIn("Bonjour", text) diff --git a/test/test_mobile_bundle_transfer.py b/test/test_mobile_bundle_transfer.py index b4c28a1..fb39c41 100644 --- a/test/test_mobile_bundle_transfer.py +++ b/test/test_mobile_bundle_transfer.py @@ -226,8 +226,8 @@ class TestTheThreshold(unittest.TestCase): class TestTheCommandLine(unittest.TestCase): def test_it_says_the_counts_and_returns_zero(self): - import io import contextlib + import io with tempfile.TemporaryDirectory() as d: tmp = Path(d) @@ -248,8 +248,8 @@ class TestTheCommandLine(unittest.TestCase): def test_a_failure_is_one_line_not_a_traceback(self): """Le message part dans un journal d'installation : une trace Python y serait illisible, et la cause noyée.""" - import io import contextlib + import io with tempfile.TemporaryDirectory() as d: tmp = Path(d) diff --git a/test/test_monitoring.py b/test/test_monitoring.py index 9e0181e..bc3441a 100644 --- a/test/test_monitoring.py +++ b/test/test_monitoring.py @@ -28,8 +28,10 @@ REPO = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, REPO) from script.analyse import check_migration_residue as residue # noqa: E402 -from script.analyse import monitoring # noqa: E402 -from script.analyse import monitoring_tui # noqa: E402 +from script.analyse import ( + monitoring, # noqa: E402 + monitoring_tui, # noqa: E402 +) class TestWhatTheProxyRefuses(unittest.TestCase): diff --git a/test/test_prompt_defaults.py b/test/test_prompt_defaults.py index 88cfc97..ab08a19 100644 --- a/test/test_prompt_defaults.py +++ b/test/test_prompt_defaults.py @@ -323,8 +323,9 @@ class TestTheMigrationUsesThatName(unittest.TestCase): source = inspect.getsource(TodoUpgrade.execute_odoo_upgrade) fenetre = source[ - source.index("database_name_from_file(") - - 400 : source.index("database_name_from_file(") + source.index("database_name_from_file(") - 400 : source.index( + "database_name_from_file(" + ) ] self.assertIn('default_database_name == "test"', fenetre) diff --git a/test/test_proxmox_form.py b/test/test_proxmox_form.py index afdeab1..89d43be 100644 --- a/test/test_proxmox_form.py +++ b/test/test_proxmox_form.py @@ -659,16 +659,22 @@ class TestLePreVolDuCacheSurProxmox(unittest.TestCase): app.action_deploy() vu["second"] = getattr(app, "result", None) - with mock.patch.object( - cache_offline, "suites_absentes", return_value=list(absentes) - ), mock.patch.object( - cache_offline, "composants_absents", return_value=[] - ), mock.patch.object( - cache_offline, "manques_hors_ligne", return_value=[] - ), mock.patch.object( - cache_offline, "paquets_absents", return_value=[] - ), mock.patch.object( - cache_offline, "miroirs_absents", return_value=[] + with ( + mock.patch.object( + cache_offline, "suites_absentes", return_value=list(absentes) + ), + mock.patch.object( + cache_offline, "composants_absents", return_value=[] + ), + mock.patch.object( + cache_offline, "manques_hors_ligne", return_value=[] + ), + mock.patch.object( + cache_offline, "paquets_absents", return_value=[] + ), + mock.patch.object( + cache_offline, "miroirs_absents", return_value=[] + ), ): asyncio.run(scenario()) return vu @@ -1109,8 +1115,8 @@ class TestLaTroisDSurProxmox(unittest.TestCase): ctx["gpu_manque"] = manque vu = {"recu": None, "notes": []} if moyen: - ctx["installer_gpu"] = ( - lambda paquets: vu.update(recu=paquets) or True + ctx["installer_gpu"] = lambda paquets: ( + vu.update(recu=paquets) or True ) ctx["sonder_gpu"] = lambda: apres @@ -1865,9 +1871,7 @@ class TestNeRienPerdreDansSshConfig(unittest.TestCase): """« Host prod-db vm-a » perdait le prod-db de l'utilisateur : le bloc partait en entier dès qu'UN de ses noms était repris.""" avant = ( - "Host prod-db vm-a\n" - " HostName db.interne\n" - " ProxyJump pve9\n" + "Host prod-db vm-a\n HostName db.interne\n ProxyJump pve9\n" ) apres = self.retirer(avant, ["vm-a"]) self.assertIn("Host prod-db\n", apres) diff --git a/test/test_qemu_ai_tools.py b/test/test_qemu_ai_tools.py index b5ead1b..7bba344 100644 --- a/test/test_qemu_ai_tools.py +++ b/test/test_qemu_ai_tools.py @@ -273,8 +273,7 @@ class LeComplementApresClone(unittest.TestCase): garde-fou, sans le moindre message.""" cmd = self._cmd() self.assertIn( - "git -C $HOME/git/erplibre config core.hooksPath" - " script/git/hooks", + "git -C $HOME/git/erplibre config core.hooksPath script/git/hooks", cmd, ) diff --git a/test/test_qemu_cache_bypass.py b/test/test_qemu_cache_bypass.py index 985599d..7519581 100644 --- a/test/test_qemu_cache_bypass.py +++ b/test/test_qemu_cache_bypass.py @@ -104,12 +104,16 @@ class TestLExceptionEstPoseeAvant(unittest.TestCase): def test_la_mac_entre_dans_le_reseau_avant_la_creation(self): args = args_neufs() runner = FauxRunner({"is-active": (0, "")}) - with mock.patch.object( - deploy_qemu.os.path, "isfile", return_value=True - ), mock.patch.object( - deploy_qemu.shutil, "which", return_value="/usr/bin/nft" - ), mock.patch.object( - deploy_qemu, "macs_deja_prises", return_value=set() + with ( + mock.patch.object( + deploy_qemu.os.path, "isfile", return_value=True + ), + mock.patch.object( + deploy_qemu.shutil, "which", return_value="/usr/bin/nft" + ), + mock.patch.object( + deploy_qemu, "macs_deja_prises", return_value=set() + ), ): mac = deploy_qemu.cache_bypass_apply(args, runner) self.assertTrue(mac, "aucune MAC retenue") @@ -124,12 +128,16 @@ class TestLExceptionEstPoseeAvant(unittest.TestCase): args = args_neufs() args.lang = "en" runner = FauxRunner({"is-active": (0, "")}) - with mock.patch.object( - deploy_qemu.os.path, "isfile", return_value=True - ), mock.patch.object( - deploy_qemu.shutil, "which", return_value="/usr/bin/nft" - ), mock.patch.object( - deploy_qemu, "macs_deja_prises", return_value=set() + with ( + mock.patch.object( + deploy_qemu.os.path, "isfile", return_value=True + ), + mock.patch.object( + deploy_qemu.shutil, "which", return_value="/usr/bin/nft" + ), + mock.patch.object( + deploy_qemu, "macs_deja_prises", return_value=set() + ), ): deploy_qemu.cache_bypass_apply(args, runner) pose = [c for c in runner.commandes if "--bypass-add" in " ".join(c)] @@ -167,12 +175,14 @@ class TestLExceptionEstPoseeAvant(unittest.TestCase): reposant la chaîne, et seul un redémarrage la repose.""" args = args_neufs() runner = FauxRunner({"is-active": (0, "")}) - with mock.patch.object( - deploy_qemu.os.path, "isfile", return_value=True - ), mock.patch.object( - deploy_qemu.shutil, "which", return_value=None - ), mock.patch.object( - deploy_qemu, "macs_deja_prises", return_value=set() + with ( + mock.patch.object( + deploy_qemu.os.path, "isfile", return_value=True + ), + mock.patch.object(deploy_qemu.shutil, "which", return_value=None), + mock.patch.object( + deploy_qemu, "macs_deja_prises", return_value=set() + ), ): deploy_qemu.cache_bypass_apply(args, runner) dit = [" ".join(c) for c in runner.commandes] @@ -270,12 +280,13 @@ class TestLeMenageDesExceptions(unittest.TestCase): from script.todo import qemu_cache_menu as menu execute = mock.MagicMock() - with mock.patch.object( - menu.os.path, "isfile", return_value=True - ), mock.patch.object( - menu.QemuCacheMenuMixin, - "_cache_bypass_orphelines", - return_value=[("52:54:00:00:00:04", "partie")], + with ( + mock.patch.object(menu.os.path, "isfile", return_value=True), + mock.patch.object( + menu.QemuCacheMenuMixin, + "_cache_bypass_orphelines", + return_value=[("52:54:00:00:00:04", "partie")], + ), ): self.assertEqual(menu.bypass_menage(execute), 1) cmd = execute.exec_command_live.call_args.args[0] @@ -296,9 +307,10 @@ class TestLeMenageDesExceptions(unittest.TestCase): from script.todo.qemu_cache_menu import QemuCacheMenuMixin as M sortie = "52:54:00:aa:bb:cc vm-une\n52:54:00:11:22:33\n\n" - with mock.patch.object( - M, "_cache_lire", return_value=sortie - ), mock.patch("os.path.isfile", return_value=True): + with ( + mock.patch.object(M, "_cache_lire", return_value=sortie), + mock.patch("os.path.isfile", return_value=True), + ): self.assertEqual( M._cache_bypass_lire(), [("52:54:00:aa:bb:cc", "vm-une"), ("52:54:00:11:22:33", "")], diff --git a/test/test_qemu_cache_charge.py b/test/test_qemu_cache_charge.py index 3fdda3b..e27d2be 100644 --- a/test/test_qemu_cache_charge.py +++ b/test/test_qemu_cache_charge.py @@ -276,18 +276,22 @@ class TestLeRapportSeClotSurUnEchec(unittest.TestCase): with tempfile.TemporaryDirectory() as rep: fichier = str(Path(rep) / "rapport.json") rapport = {"_fichier": fichier, "vms": []} - with mock.patch.object(QC, "dire"), mock.patch.object( - QC, "noter_uuid" - ), mock.patch.object( - QC, - "deployer", - return_value="" if echoue == "deployer" else "10.0.0.1", - ), mock.patch.object( - QC, "attendre_ssh", return_value=echoue != "attendre_ssh" - ), mock.patch.object( - QC, - "poser_les_paquets", - return_value=echoue != "poser_les_paquets", + with ( + mock.patch.object(QC, "dire"), + mock.patch.object(QC, "noter_uuid"), + mock.patch.object( + QC, + "deployer", + return_value="" if echoue == "deployer" else "10.0.0.1", + ), + mock.patch.object( + QC, "attendre_ssh", return_value=echoue != "attendre_ssh" + ), + mock.patch.object( + QC, + "poser_les_paquets", + return_value=echoue != "poser_les_paquets", + ), ): code = QC._boucle(args, rapport, None, "", 0) with open(fichier, encoding="utf-8") as fh: @@ -347,22 +351,24 @@ class TestLaContreEpreuveNommeSonEtape(unittest.TestCase): with tempfile.TemporaryDirectory() as rep: fichier = str(Path(rep) / "rapport.json") rapport = {"_fichier": fichier, "vms": []} - with mock.patch.object(QC, "dire"), mock.patch.object( - QC, "noter_uuid" - ), mock.patch.object(QC, "eteindre"), mock.patch.object( - QC, "verdict", return_value=True - ), mock.patch.object( - QC, "couper_lamont", return_value=True - ), mock.patch.object( - QC, "rebrancher_lamont", create=True - ), mock.patch.object( - QC, "deployer", side_effect=selon("deployer", "10.0.0.1") - ), mock.patch.object( - QC, "attendre_ssh", side_effect=selon("attendre_ssh", True) - ), mock.patch.object( - QC, - "poser_les_paquets", - side_effect=selon("poser_les_paquets", True), + with ( + mock.patch.object(QC, "dire"), + mock.patch.object(QC, "noter_uuid"), + mock.patch.object(QC, "eteindre"), + mock.patch.object(QC, "verdict", return_value=True), + mock.patch.object(QC, "couper_lamont", return_value=True), + mock.patch.object(QC, "rebrancher_lamont", create=True), + mock.patch.object( + QC, "deployer", side_effect=selon("deployer", "10.0.0.1") + ), + mock.patch.object( + QC, "attendre_ssh", side_effect=selon("attendre_ssh", True) + ), + mock.patch.object( + QC, + "poser_les_paquets", + side_effect=selon("poser_les_paquets", True), + ), ): code = QC._boucle(args, rapport, None, "", 0) with open(fichier, encoding="utf-8") as fh: @@ -389,8 +395,9 @@ class TestLaTroisiemeVmNaitHorsLigne(unittest.TestCase): def commande(self, **kw): from unittest import mock - with mock.patch.object(QC, "dire") as dit, mock.patch.object( - QC, "cle_publique", return_value="/tmp/cle.pub" + with ( + mock.patch.object(QC, "dire") as dit, + mock.patch.object(QC, "cle_publique", return_value="/tmp/cle.pub"), ): QC.deployer("vm", None, dry_run=True, **kw) return " ".join(str(a) for c in dit.call_args_list for a in c.args) @@ -440,14 +447,14 @@ class TestPlusieursSystemes(unittest.TestCase): appels.append((a.distro, a.version)) return (codes.pop(0) if codes else 0), "" - with mock.patch.object( - QC, "une_campagne", side_effect=campagne - ), mock.patch.object( - QC, - "detruire", - side_effect=lambda *x, **k: appels.append("détruire"), - ), mock.patch( - "builtins.print" + with ( + mock.patch.object(QC, "une_campagne", side_effect=campagne), + mock.patch.object( + QC, + "detruire", + side_effect=lambda *x, **k: appels.append("détruire"), + ), + mock.patch("builtins.print"), ): code = QC.main(argv) return code, appels @@ -517,22 +524,24 @@ class TestUneVmMesureeEstEteinte(unittest.TestCase): ) with tempfile.TemporaryDirectory() as rep: rapport = {"_fichier": str(Path(rep) / "r.json"), "vms": []} - with mock.patch.object(QC, "dire"), mock.patch.object( - QC, "noter_uuid" - ), mock.patch.object( - QC, "deployer", return_value="10.0.0.1" - ), mock.patch.object( - QC, "attendre_ssh", return_value=True - ), mock.patch.object( - QC, - "poser_les_paquets", - side_effect=lambda *a, **k: ordre.append("paquets") or True, - ), mock.patch.object( - QC, - "eteindre", - side_effect=lambda nom, *a, **k: ordre.append(nom), - ), mock.patch.object( - QC, "verdict", return_value=True + with ( + mock.patch.object(QC, "dire"), + mock.patch.object(QC, "noter_uuid"), + mock.patch.object(QC, "deployer", return_value="10.0.0.1"), + mock.patch.object(QC, "attendre_ssh", return_value=True), + mock.patch.object( + QC, + "poser_les_paquets", + side_effect=lambda *a, **k: ( + ordre.append("paquets") or True + ), + ), + mock.patch.object( + QC, + "eteindre", + side_effect=lambda nom, *a, **k: ordre.append(nom), + ), + mock.patch.object(QC, "verdict", return_value=True), ): QC._boucle(args, rapport, None, "", 0) base = QC.base_des_noms(args) @@ -552,9 +561,10 @@ class TestUneVmMesureeEstEteinte(unittest.TestCase): def test_a_blanc_rien_n_est_lance(self): from unittest import mock - with mock.patch.object(QC, "dire"), mock.patch.object( - QC, "executer" - ) as ex: + with ( + mock.patch.object(QC, "dire"), + mock.patch.object(QC, "executer") as ex, + ): QC.eteindre("vm-essai", None, dry_run=True) ex.assert_not_called() diff --git a/test/test_qemu_cache_form.py b/test/test_qemu_cache_form.py index 65ce1f8..7e6dfb6 100644 --- a/test/test_qemu_cache_form.py +++ b/test/test_qemu_cache_form.py @@ -184,8 +184,9 @@ class TestLInstallationNeSannonceQueSiElleAEuLieu(unittest.TestCase): todo = TODO.__new__(TODO) todo.execute = mock.Mock() todo.execute.exec_command_live = mock.Mock(return_value=code) - with mock.patch("builtins.input", return_value=""), mock.patch.object( - click, "confirm", return_value=True + with ( + mock.patch("builtins.input", return_value=""), + mock.patch.object(click, "confirm", return_value=True), ): with contextlib.redirect_stdout(io.StringIO()) as sortie: todo._deploy_qemu_cache() @@ -564,8 +565,7 @@ class TestLaSectionReseau(unittest.TestCase): self.assertIs( vu["defaut"], False, - "cochée d'avance, elle couperait l'amont sans qu'on l'ait" - " demandé", + "cochée d'avance, elle couperait l'amont sans qu'on l'ait demandé", ) self.assertIs(vu["spec"], True) @@ -759,16 +759,24 @@ class TestLaSectionReseau(unittest.TestCase): # nomme la suite absente, et elle seule. Un verdict laissé libre lit # la machine qui exécute les tests — son magasin, ses miroirs — et # l'épreuve passerait alors pour une raison qui n'est pas la sienne. - with mock.patch.object( - cache_offline, "suites_absentes", lambda vms: [("ubuntu", "26.04")] - ), mock.patch.object( - cache_offline, "composants_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "manques_hors_ligne", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "paquets_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "miroirs_absents", lambda *a, **k: [] + with ( + mock.patch.object( + cache_offline, + "suites_absentes", + lambda vms: [("ubuntu", "26.04")], + ), + mock.patch.object( + cache_offline, "composants_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "manques_hors_ligne", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "paquets_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "miroirs_absents", lambda *a, **k: [] + ), ): asyncio.run(scenario()) self.assertIsNone( @@ -804,16 +812,22 @@ class TestLaSectionReseau(unittest.TestCase): # qu'un cache pourvu ne retarde personne. En laisser un libre le fait # lire la machine qui exécute les tests — son magasin, ses miroirs, # son journal — et l'épreuve échoue selon le poste. - with mock.patch.object( - cache_offline, "suites_absentes", lambda vms: [] - ), mock.patch.object( - cache_offline, "composants_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "manques_hors_ligne", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "paquets_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "miroirs_absents", lambda *a, **k: [] + with ( + mock.patch.object( + cache_offline, "suites_absentes", lambda vms: [] + ), + mock.patch.object( + cache_offline, "composants_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "manques_hors_ligne", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "paquets_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "miroirs_absents", lambda *a, **k: [] + ), ): asyncio.run(scenario()) self.assertIsNotNone( diff --git a/test/test_qemu_cache_langue.py b/test/test_qemu_cache_langue.py index cc08cd9..4d8e09d 100644 --- a/test/test_qemu_cache_langue.py +++ b/test/test_qemu_cache_langue.py @@ -57,9 +57,12 @@ class TestCeQueLeMenuLit(unittest.TestCase): vu["cmd"] = cmd return "objets : 3\ndépôts git : 12 en miroir, 4.2 Gio\n" - with mock.patch.object( - menu.QemuCacheMenuMixin, "_cache_lire", staticmethod(faux_lire) - ), mock.patch.object(menu, "get_lang", return_value="en"): + with ( + mock.patch.object( + menu.QemuCacheMenuMixin, "_cache_lire", staticmethod(faux_lire) + ), + mock.patch.object(menu, "get_lang", return_value="en"), + ): rendu = menu.QemuCacheMenuMixin._cache_miroir_occupation() self.assertIn("--lang fr", vu["cmd"]) self.assertEqual(rendu, ("12", "4.2 Gio")) diff --git a/test/test_qemu_cache_mesure.py b/test/test_qemu_cache_mesure.py index cc1b74b..613d5a4 100644 --- a/test/test_qemu_cache_mesure.py +++ b/test/test_qemu_cache_mesure.py @@ -317,11 +317,14 @@ class TestCeQuIlFautDefaire(unittest.TestCase): @contextlib.contextmanager def isole(): - with mock.patch.object( - QC.os.path, - "expanduser", - lambda p: d if "longtest" in p else p, - ), mock.patch.object(QC, "machines_vivantes", return_value=[]): + with ( + mock.patch.object( + QC.os.path, + "expanduser", + lambda p: d if "longtest" in p else p, + ), + mock.patch.object(QC, "machines_vivantes", return_value=[]), + ): yield return d, isole() @@ -512,10 +515,13 @@ class TestUnPrefixeParMode(unittest.TestCase): """Les rapports sont bornés : une machine plus ancienne que la fenêtre ne serait jamais défaite et bloquerait tous les essais suivants.""" vivantes = "el-cache-test-9\nel-offline-test-9\nel-no-cache-test-9\n" - with unittest.mock.patch.object( - QC, "executer", return_value=(0, vivantes) - ), unittest.mock.patch.object( - QC.os.path, "expanduser", return_value="/inexistant" + with ( + unittest.mock.patch.object( + QC, "executer", return_value=(0, vivantes) + ), + unittest.mock.patch.object( + QC.os.path, "expanduser", return_value="/inexistant" + ), ): machines, _ = QC.machines_a_defaire() self.assertEqual( @@ -577,9 +583,12 @@ class TestLeGainSeCalculeParCondition(unittest.TestCase): import io as _io tampon = _io.StringIO() - with unittest.mock.patch.object( - QC, "rapports_recents", return_value=rapports - ), contextlib.redirect_stdout(tampon): + with ( + unittest.mock.patch.object( + QC, "rapports_recents", return_value=rapports + ), + contextlib.redirect_stdout(tampon), + ): QC.rapport_comparatif() return tampon.getvalue() diff --git a/test/test_qemu_cache_offline.py b/test/test_qemu_cache_offline.py index d396741..97dc603 100644 --- a/test/test_qemu_cache_offline.py +++ b/test/test_qemu_cache_offline.py @@ -450,20 +450,24 @@ class TestLeDiagnosticVoitLaCoupure(unittest.TestCase): menu = self._menu(self.TABLE_POSEE) faux = menu.__new__(menu) faux._cache_actif = lambda: True - with mock.patch("os.path.isfile", return_value=True), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_par_machine", - return_value=[], - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin" - "._cache_compte_issues", - return_value={}, - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin" - "._cache_bypass_lire", - return_value=[], - ), contextlib.redirect_stdout( - _io.StringIO() - ) as sortie: + with ( + mock.patch("os.path.isfile", return_value=True), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_par_machine", + return_value=[], + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin" + "._cache_compte_issues", + return_value={}, + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin" + "._cache_bypass_lire", + return_value=[], + ), + contextlib.redirect_stdout(_io.StringIO()) as sortie, + ): faux._cache_diagnostic() ecrit = sortie.getvalue() self.assertIn( @@ -853,13 +857,13 @@ class TestLaLeveeConfieeAuGuet(_SansSysteme, unittest.TestCase): todo._qemu_shell = shell todo._qemu_import_module = lambda: None todo._qemu_erplibre_remote_cmd = lambda *a, **kw: "true" - with mock.patch.object( - mon, "launch_installs", lance - ), mock.patch.object( - mon, "run_monitor", lambda m: vu["ordre"].append("tableau") - ), contextlib.redirect_stdout( - io.StringIO() - ) as sortie: + with ( + mock.patch.object(mon, "launch_installs", lance), + mock.patch.object( + mon, "run_monitor", lambda m: vu["ordre"].append("tableau") + ), + contextlib.redirect_stdout(io.StringIO()) as sortie, + ): todo._qemu_install_erplibre_monitored( ["vm-a"], "develop", @@ -908,8 +912,8 @@ class TestLaLeveeConfieeAuGuet(_SansSysteme, unittest.TestCase): todo = _todo() todo._qemu_shell = hote.shell vus = [] - todo._qemu_install_erplibre_monitored = ( - lambda *a, **kw: vus.append(kw) + todo._qemu_install_erplibre_monitored = lambda *a, **kw: ( + vus.append(kw) ) todo._qemu_resolve_ips = lambda names, labels=None: {} with contextlib.redirect_stdout(io.StringIO()): @@ -1225,9 +1229,10 @@ class TestLecartHorsLigne(_SansSysteme, unittest.TestCase): "parallelism": 1, "offline": hors_ligne, } - with mock.patch( - "subprocess.run", self._git(**git) - ), contextlib.redirect_stdout(io.StringIO()) as sortie: + with ( + mock.patch("subprocess.run", self._git(**git)), + contextlib.redirect_stdout(io.StringIO()) as sortie, + ): todo._qemu_print_recap(spec, []) return sortie.getvalue() @@ -1791,12 +1796,10 @@ class TestLeDebutDuDeploiementDansLaSession(_SansSysteme, unittest.TestCase): tmp = tempfile.TemporaryDirectory() self.addCleanup(tmp.cleanup) - with mock.patch.object( - mon, "session_dir", lambda: Path(tmp.name) - ), mock.patch.object( - mon, "_launch_one", lambda *a, **k: None - ), mock.patch.object( - mon, "local_uuid", lambda nom: "" + with ( + mock.patch.object(mon, "session_dir", lambda: Path(tmp.name)), + mock.patch.object(mon, "_launch_one", lambda *a, **k: None), + mock.patch.object(mon, "local_uuid", lambda nom: ""), ): chemin = mon.launch_installs( [dict(self.VM)], "develop", "true", **kw @@ -1846,12 +1849,10 @@ class TestLeDebutDuDeploiementDansLaSession(_SansSysteme, unittest.TestCase): todo._qemu_shell = lambda cmd, timeout=60: 1 todo._qemu_import_module = lambda: None todo._qemu_erplibre_remote_cmd = lambda *a, **k: "true" - with mock.patch.object( - mon, "launch_installs", lance - ), mock.patch.object( - mon, "run_monitor", lambda m: None - ), contextlib.redirect_stdout( - io.StringIO() + with ( + mock.patch.object(mon, "launch_installs", lance), + mock.patch.object(mon, "run_monitor", lambda m: None), + contextlib.redirect_stdout(io.StringIO()), ): todo._qemu_install_erplibre_monitored( ["vm-a"], "develop", {"vm-a": "192.0.2.10"}, **kw diff --git a/test/test_qemu_cache_preflight.py b/test/test_qemu_cache_preflight.py index 2dbc736..c3cb0fb 100644 --- a/test/test_qemu_cache_preflight.py +++ b/test/test_qemu_cache_preflight.py @@ -983,9 +983,10 @@ class TestLeComblement(SansSysteme): def combler(self, confirmer=True): sortie = io.StringIO() faux = Faux() - with mock.patch( - "click.confirm", return_value=confirmer - ) as confirme, contextlib.redirect_stdout(sortie): + with ( + mock.patch("click.confirm", return_value=confirmer) as confirme, + contextlib.redirect_stdout(sortie), + ): faux._cache_combler() return sortie.getvalue(), confirme, faux @@ -1383,23 +1384,29 @@ class TestLeMenuLitLaCoupureEtLeGuet(SansSysteme): binaire.write_text("") sortie = io.StringIO() faux = Faux() - with mock.patch.object(menu, "CACHE_BIN", str(binaire)), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_amont_coupe", - classmethod(lambda cls: coupe), - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_guet_actif", - classmethod(lambda cls: guet), - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_par_machine", - return_value=[], - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_compte_issues", - return_value={}, - ), mock.patch( - "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_bypass_lire", - return_value=[], - ), contextlib.redirect_stdout( - sortie + with ( + mock.patch.object(menu, "CACHE_BIN", str(binaire)), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_amont_coupe", + classmethod(lambda cls: coupe), + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_guet_actif", + classmethod(lambda cls: guet), + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_par_machine", + return_value=[], + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_compte_issues", + return_value={}, + ), + mock.patch( + "script.todo.qemu_cache_menu.QemuCacheMenuMixin._cache_bypass_lire", + return_value=[], + ), + contextlib.redirect_stdout(sortie), ): faux._cache_diagnostic() return sortie.getvalue() @@ -1488,13 +1495,17 @@ class TestLeRemplissageDesMiroirs(SansSysteme): def test_le_menu_lance_cette_commande(self): maison = self.dossier / "maison" vraie = os.path.expanduser - with mock.patch.object( - menu, "CACHE_UNITE", self.unite_de_linstallateur() - ), mock.patch( - "os.path.expanduser", - lambda p: str(maison) + p[1:] if p.startswith("~") else vraie(p), - ), mock.patch( - "click.confirm", return_value=True + with ( + mock.patch.object( + menu, "CACHE_UNITE", self.unite_de_linstallateur() + ), + mock.patch( + "os.path.expanduser", + lambda p: ( + str(maison) + p[1:] if p.startswith("~") else vraie(p) + ), + ), + mock.patch("click.confirm", return_value=True), ): faux = Faux() with contextlib.redirect_stdout(io.StringIO()): @@ -1572,16 +1583,22 @@ class TestLAvertissementAvantLaCoupure(SansSysteme): # ce qu'elle nomme. Un verdict laissé libre lit la machine qui # exécute les tests — son magasin, ses miroirs — et le résultat # change alors d'un poste à l'autre. - with mock.patch.object( - cache_offline, "suites_absentes", lambda vms: absentes - ), mock.patch.object( - cache_offline, "manques_hors_ligne", lambda vms: manques - ), mock.patch.object( - cache_offline, "composants_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "paquets_absents", lambda *a, **k: [] - ), mock.patch.object( - cache_offline, "miroirs_absents", lambda *a, **k: [] + with ( + mock.patch.object( + cache_offline, "suites_absentes", lambda vms: absentes + ), + mock.patch.object( + cache_offline, "manques_hors_ligne", lambda vms: manques + ), + mock.patch.object( + cache_offline, "composants_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "paquets_absents", lambda *a, **k: [] + ), + mock.patch.object( + cache_offline, "miroirs_absents", lambda *a, **k: [] + ), ): asyncio.run(scenario()) return vu diff --git a/test/test_qemu_deploy_failure_output.py b/test/test_qemu_deploy_failure_output.py index 07df6fc..2f20a1a 100644 --- a/test/test_qemu_deploy_failure_output.py +++ b/test/test_qemu_deploy_failure_output.py @@ -54,12 +54,14 @@ class FailureOutput(unittest.TestCase): stdout = sortie stderr = "" - with mock.patch( - "script.todo.qemu_deploy.subprocess.run", return_value=Res() - ), mock.patch.object( - self.todo, "_fmt_dur", return_value="1s" - ), mock.patch.object( - self.todo, "_qemu_save_failure_log", return_value="/tmp/x.log" + with ( + mock.patch( + "script.todo.qemu_deploy.subprocess.run", return_value=Res() + ), + mock.patch.object(self.todo, "_fmt_dur", return_value="1s"), + mock.patch.object( + self.todo, "_qemu_save_failure_log", return_value="/tmp/x.log" + ), ): buf = io.StringIO() with redirect_stdout(buf): diff --git a/test/test_qemu_deploy_monitor.py b/test/test_qemu_deploy_monitor.py index ea70e33..436fc81 100644 --- a/test/test_qemu_deploy_monitor.py +++ b/test/test_qemu_deploy_monitor.py @@ -247,10 +247,8 @@ class TestLeJournal(unittest.TestCase): # « **kw » et non une liste figée : chaque paramètre ajouté au # lanceur (comme « pve ») casserait sinon ce test, qui ne vérifie # pourtant que le prologue du journal. - mon._launch_one = ( - lambda ip, cmd, log, name="", installs=True, **kw: vus.append( - installs - ) + mon._launch_one = lambda ip, cmd, log, name="", installs=True, **kw: ( + vus.append(installs) ) # session_dir détournée : sans cela le test écrivait de VRAIES sessions # dans ~/.erplibre/qemu-install, qui polluaient l'historique que diff --git a/test/test_qemu_egl_fallback.py b/test/test_qemu_egl_fallback.py index 349fda0..dd91657 100644 --- a/test/test_qemu_egl_fallback.py +++ b/test/test_qemu_egl_fallback.py @@ -91,13 +91,11 @@ class Repli(unittest.TestCase): return (code, sortie) return None - with mock.patch.object( - DQ, "host_arch", return_value="amd64" - ), mock.patch.object( - DQ, "kvm_available", return_value=True - ), mock.patch.object( - DQ, "os" - ) as faux_os: + with ( + mock.patch.object(DQ, "host_arch", return_value="amd64"), + mock.patch.object(DQ, "kvm_available", return_value=True), + mock.patch.object(DQ, "os") as faux_os, + ): faux_os.getuid.return_value = 1000 faux_os.makedirs.return_value = None buf = io.StringIO() @@ -168,13 +166,11 @@ class Repli(unittest.TestCase): lances.append(list(cmd)) return resultat if capture else None - with mock.patch.object( - DQ, "host_arch", return_value="amd64" - ), mock.patch.object( - DQ, "kvm_available", return_value=True - ), mock.patch.object( - DQ, "os" - ) as faux_os: + with ( + mock.patch.object(DQ, "host_arch", return_value="amd64"), + mock.patch.object(DQ, "kvm_available", return_value=True), + mock.patch.object(DQ, "os") as faux_os, + ): faux_os.getuid.return_value = 1000 with redirect_stdout(io.StringIO()): DQ.virt_install( diff --git a/test/test_qemu_emulator_menu.py b/test/test_qemu_emulator_menu.py index 0c80044..8c399b4 100644 --- a/test/test_qemu_emulator_menu.py +++ b/test/test_qemu_emulator_menu.py @@ -191,15 +191,17 @@ class _MenuCase(unittest.TestCase): return _run_ok() it = iter(answers) - with mock.patch("subprocess.run", side_effect=fake_run), mock.patch( - "builtins.input", lambda *a: next(it) - ), mock.patch.object( - TODO, "_port_in_use", staticmethod(lambda p: port_taken) - ), mock.patch( - "script.todo.todo.time.sleep", lambda *a: None - ), mock.patch( - "sys.stdout", new_callable=__import__("io").StringIO - ) as out: + with ( + mock.patch("subprocess.run", side_effect=fake_run), + mock.patch("builtins.input", lambda *a: next(it)), + mock.patch.object( + TODO, "_port_in_use", staticmethod(lambda p: port_taken) + ), + mock.patch("script.todo.todo.time.sleep", lambda *a: None), + mock.patch( + "sys.stdout", new_callable=__import__("io").StringIO + ) as out, + ): self.todo._qemu_emulator_menu() return out.getvalue(), self.calls @@ -365,15 +367,17 @@ class TestScrcpyTunnel(unittest.TestCase): return _run_ok(returncode=rc, stderr="refus") it = iter(answers) - with mock.patch("subprocess.run", side_effect=fake_run), mock.patch( - "builtins.input", lambda *a: next(it) - ), mock.patch.object( - TODO, "_port_in_use", staticmethod(lambda p: port_taken) - ), mock.patch.dict( - "os.environ", {"USER": "poste"} - ), mock.patch( - "sys.stdout", new_callable=__import__("io").StringIO - ) as out: + with ( + mock.patch("subprocess.run", side_effect=fake_run), + mock.patch("builtins.input", lambda *a: next(it)), + mock.patch.object( + TODO, "_port_in_use", staticmethod(lambda p: port_taken) + ), + mock.patch.dict("os.environ", {"USER": "poste"}), + mock.patch( + "sys.stdout", new_callable=__import__("io").StringIO + ) as out, + ): self.todo._qemu_scrcpy_tunnel(name, src, started=started) return out.getvalue(), self.calls diff --git a/test/test_qemu_gpu.py b/test/test_qemu_gpu.py index f20ba4a..57905bf 100644 --- a/test/test_qemu_gpu.py +++ b/test/test_qemu_gpu.py @@ -415,8 +415,8 @@ class TestMenuGlue(unittest.TestCase): todo._qemu_net_choices = lambda: ["network:default"] todo.launched = [] todo.execute = mock.Mock() - todo.execute.exec_command_live = ( - lambda cmd, **kw: todo.launched.append(cmd) + todo.execute.exec_command_live = lambda cmd, **kw: ( + todo.launched.append(cmd) ) return todo @@ -458,14 +458,19 @@ class TestMenuGlue(unittest.TestCase): todo._qemu_hw_form = lambda rows, node, nets=None: { "vm-a": {"vcpus": 4, "ram": 8192, "gpu": True} } - with mock.patch( - "script.todo.qemu_privilege.libvirt_reachable", - return_value=joignable, - ), mock.patch( - "script.todo.qemu_privilege.os.geteuid", return_value=1000 - ), mock.patch( - "script.todo.qemu_privilege.shutil.which", - return_value="/usr/bin/virsh", + with ( + mock.patch( + "script.todo.qemu_privilege.libvirt_reachable", + return_value=joignable, + ), + mock.patch( + "script.todo.qemu_privilege.os.geteuid", + return_value=1000, + ), + mock.patch( + "script.todo.qemu_privilege.shutil.which", + return_value="/usr/bin/virsh", + ), ): self._run(todo, ["vm-a"], ["o"]) self.assertTrue(todo.launched) diff --git a/test/test_qemu_host_network.py b/test/test_qemu_host_network.py index 11e5498..c7fd14d 100644 --- a/test/test_qemu_host_network.py +++ b/test/test_qemu_host_network.py @@ -327,23 +327,25 @@ class LOrdreDesGestes(unittest.TestCase): suite_cidr = list(cidrs) etats = [etat_initial, (actif_apres, etat_initial[1])] - with mock.patch.object( - DQ, - "network_cidr", - side_effect=lambda *a: ( - suite_cidr.pop(0) if len(suite_cidr) > 1 else suite_cidr[0] + with ( + mock.patch.object( + DQ, + "network_cidr", + side_effect=lambda *a: ( + suite_cidr.pop(0) if len(suite_cidr) > 1 else suite_cidr[0] + ), ), - ), mock.patch.object( - DQ, "host_networks", return_value=reseaux(*hote) - ), mock.patch.object( - DQ, "libvirt_networks_cidrs", return_value=[] - ), mock.patch.object( - DQ, "virsh_out", return_value=XML_DEFAUT - ), mock.patch.object( - DQ, - "network_state", - side_effect=lambda *a: ( - etats.pop(0) if len(etats) > 1 else etats[0] + mock.patch.object( + DQ, "host_networks", return_value=reseaux(*hote) + ), + mock.patch.object(DQ, "libvirt_networks_cidrs", return_value=[]), + mock.patch.object(DQ, "virsh_out", return_value=XML_DEFAUT), + mock.patch.object( + DQ, + "network_state", + side_effect=lambda *a: ( + etats.pop(0) if len(etats) > 1 else etats[0] + ), ), ): with redirect_stdout(io.StringIO()) as sortie: diff --git a/test/test_qemu_monitor_pve.py b/test/test_qemu_monitor_pve.py index 0d1d3b2..8517a90 100644 --- a/test/test_qemu_monitor_pve.py +++ b/test/test_qemu_monitor_pve.py @@ -872,8 +872,7 @@ class TestLaColonneOdoo(unittest.TestCase): def test_the_answer_is_read_per_address(self): sortie = ( - "[]\n---ERPLIBRE-DU---\n---ERPLIBRE-ODOO---\n" - "ODOO 10.10.10.151\n" + "[]\n---ERPLIBRE-DU---\n---ERPLIBRE-ODOO---\nODOO 10.10.10.151\n" ) self.assertEqual(mon.parse_odoo_probe(sortie), {"10.10.10.151"}) diff --git a/test/test_qemu_monitor_vmstats.py b/test/test_qemu_monitor_vmstats.py index 6066280..069304a 100644 --- a/test/test_qemu_monitor_vmstats.py +++ b/test/test_qemu_monitor_vmstats.py @@ -319,16 +319,20 @@ class TestEcranMonte(unittest.IsolatedAsyncioTestCase): async def _monte(self, taille=(150, 24)): from textual.widgets import DataTable, Static - with mock.patch.object( - mon, "read_domstats", lambda: self.stats - ), mock.patch.object( - mon, "arm_balloon", lambda names: None - ), mock.patch.object( - mon, - "virsh_domstates", - lambda: {MIGRATION: "running", "erplibre-ubuntu-2604": "running"}, - ), mock.patch.object( - mon, "_port_open", lambda ip, port=8069, timeout=0.5: False + with ( + mock.patch.object(mon, "read_domstats", lambda: self.stats), + mock.patch.object(mon, "arm_balloon", lambda names: None), + mock.patch.object( + mon, + "virsh_domstates", + lambda: { + MIGRATION: "running", + "erplibre-ubuntu-2604": "running", + }, + ), + mock.patch.object( + mon, "_port_open", lambda ip, port=8069, timeout=0.5: False + ), ): app = mon.run_monitor(str(self.manifest), run_app=False) async with app.run_test(size=taille) as pilot: diff --git a/test/test_qemu_network_recreate.py b/test/test_qemu_network_recreate.py index 130f657..3d2501b 100644 --- a/test/test_qemu_network_recreate.py +++ b/test/test_qemu_network_recreate.py @@ -142,14 +142,11 @@ def lancer( runner.dry_run = False runner.use_sudo = False runner.run.side_effect = parc.run - with mock.patch.object( - NQ.DQ, "virsh_out", side_effect=parc.virsh_out - ), mock.patch.object( - NQ.DQ, "host_networks", return_value=reseaux(*hote) - ), mock.patch.object( - NQ.DQ, "libvirt_networks_cidrs", return_value=[] - ), mock.patch.object( - NQ.DQ, "network_state", return_value=etat_reseau + with ( + mock.patch.object(NQ.DQ, "virsh_out", side_effect=parc.virsh_out), + mock.patch.object(NQ.DQ, "host_networks", return_value=reseaux(*hote)), + mock.patch.object(NQ.DQ, "libvirt_networks_cidrs", return_value=[]), + mock.patch.object(NQ.DQ, "network_state", return_value=etat_reseau), ): with redirect_stdout(io.StringIO()) as sortie: code = NQ.recreer(args or Args(), runner) diff --git a/test/test_qemu_orphan_disk.py b/test/test_qemu_orphan_disk.py index 1c4562c..31819c6 100644 --- a/test/test_qemu_orphan_disk.py +++ b/test/test_qemu_orphan_disk.py @@ -55,8 +55,9 @@ class PropositionEffacement(unittest.TestCase): self.todo._qemu_orphan_disks = faux_orphans self.todo._cleanup_delete_files = effacer - with mock.patch("builtins.input", lambda *a: next(it, "")), mock.patch( - "builtins.print" + with ( + mock.patch("builtins.input", lambda *a: next(it, "")), + mock.patch("builtins.print"), ): ok = self.todo._qemu_offer_orphan_removal(["vm-a", "vm-b"]) return ok, self.efface @@ -80,8 +81,9 @@ class PropositionEffacement(unittest.TestCase): for n in names if n == "vm-a" ] - with mock.patch("builtins.input", return_value="n"), mock.patch( - "builtins.print" + with ( + mock.patch("builtins.input", return_value="n"), + mock.patch("builtins.print"), ): self.assertFalse( self.todo._qemu_offer_orphan_removal(["vm-a", "vm-b"]) @@ -93,8 +95,9 @@ class PropositionEffacement(unittest.TestCase): for n in names if n == "vm-a" ] - with mock.patch("builtins.input", return_value="o"), mock.patch( - "builtins.print" + with ( + mock.patch("builtins.input", return_value="o"), + mock.patch("builtins.print"), ): self.assertTrue(self.todo._qemu_offer_orphan_removal(["vm-a"])) @@ -110,8 +113,9 @@ class PropositionEffacement(unittest.TestCase): self.todo._cleanup_delete_files = lambda t_, items, p: vus.extend( items ) - with mock.patch("builtins.input", return_value="n"), mock.patch( - "builtins.print" + with ( + mock.patch("builtins.input", return_value="n"), + mock.patch("builtins.print"), ): self.todo._qemu_offer_orphan_removal(["vm-a"]) self.assertEqual([(4096, chemin)], vus) @@ -122,8 +126,9 @@ class PropositionEffacement(unittest.TestCase): self.todo._cleanup_delete_files = lambda t_, items, p: vus.extend( items ) - with mock.patch("builtins.input", return_value="n"), mock.patch( - "builtins.print" + with ( + mock.patch("builtins.input", return_value="n"), + mock.patch("builtins.print"), ): self.todo._qemu_offer_orphan_removal(["vm-a"]) self.assertEqual([(0, "/nulle/part")], vus) diff --git a/test/test_qemu_privilege.py b/test/test_qemu_privilege.py index 9f66ede..f1f1a9a 100644 --- a/test/test_qemu_privilege.py +++ b/test/test_qemu_privilege.py @@ -45,43 +45,55 @@ class Sondage(unittest.TestCase): return mock.patch.object(qp.subprocess, "run", return_value=Res()) def test_reachable_means_no_sudo(self): - with mock.patch.object( - qp.shutil, "which", return_value="/usr/bin/virsh" - ), mock.patch.object(qp.os, "geteuid", return_value=1000), self._sonde( - 0 + with ( + mock.patch.object( + qp.shutil, "which", return_value="/usr/bin/virsh" + ), + mock.patch.object(qp.os, "geteuid", return_value=1000), + self._sonde(0), ): self.assertFalse(qp.needs_sudo()) self.assertEqual(qp.sudo_prefix(), "") def test_unreachable_means_sudo(self): - with mock.patch.object( - qp.shutil, "which", return_value="/usr/bin/virsh" - ), mock.patch.object(qp.os, "geteuid", return_value=1000), self._sonde( - 1 + with ( + mock.patch.object( + qp.shutil, "which", return_value="/usr/bin/virsh" + ), + mock.patch.object(qp.os, "geteuid", return_value=1000), + self._sonde(1), ): self.assertTrue(qp.needs_sudo()) self.assertEqual(qp.sudo_prefix(), "sudo ") def test_root_never_needs_sudo(self): - with mock.patch.object( - qp.shutil, "which", return_value="/usr/bin/virsh" - ), mock.patch.object(qp.os, "geteuid", return_value=0), self._sonde(1): + with ( + mock.patch.object( + qp.shutil, "which", return_value="/usr/bin/virsh" + ), + mock.patch.object(qp.os, "geteuid", return_value=0), + self._sonde(1), + ): self.assertFalse(qp.needs_sudo()) def test_without_virsh_no_password_prompt(self): """Demander un mot de passe pour lancer une commande introuvable ne mène nulle part : l'échec doit être « command not found ».""" - with mock.patch.object( - qp.shutil, "which", return_value=None - ), mock.patch.object(qp.os, "geteuid", return_value=1000): + with ( + mock.patch.object(qp.shutil, "which", return_value=None), + mock.patch.object(qp.os, "geteuid", return_value=1000), + ): self.assertFalse(qp.needs_sudo()) def test_the_probe_runs_once(self): """Chaque entrée de menu la demande : un virsh par commande se verrait.""" - with mock.patch.object( - qp.shutil, "which", return_value="/usr/bin/virsh" - ), mock.patch.object(qp.os, "geteuid", return_value=1000): + with ( + mock.patch.object( + qp.shutil, "which", return_value="/usr/bin/virsh" + ), + mock.patch.object(qp.os, "geteuid", return_value=1000), + ): with self._sonde(0) as run: for _ in range(5): qp.needs_sudo() @@ -90,12 +102,14 @@ class Sondage(unittest.TestCase): def test_a_dead_probe_falls_back_on_sudo(self): """Un virsh qui n'arrive pas au bout ne prouve pas l'accès : mieux vaut une invite de mot de passe qu'une commande refusée.""" - with mock.patch.object( - qp.shutil, "which", return_value="/usr/bin/virsh" - ), mock.patch.object( - qp.os, "geteuid", return_value=1000 - ), mock.patch.object( - qp.subprocess, "run", side_effect=OSError("boom") + with ( + mock.patch.object( + qp.shutil, "which", return_value="/usr/bin/virsh" + ), + mock.patch.object(qp.os, "geteuid", return_value=1000), + mock.patch.object( + qp.subprocess, "run", side_effect=OSError("boom") + ), ): self.assertTrue(qp.needs_sudo()) @@ -109,9 +123,12 @@ class AvertissementAvantInstallation(unittest.TestCase): qp.reset_cache() def _rendu(self, joignable, declare, actif): - with mock.patch.object( - qp, "libvirt_reachable", return_value=joignable - ), mock.patch.object(qp, "group_state", return_value=(declare, actif)): + with ( + mock.patch.object(qp, "libvirt_reachable", return_value=joignable), + mock.patch.object( + qp, "group_state", return_value=(declare, actif) + ), + ): buf = io.StringIO() with redirect_stdout(buf): self.todo._qemu_warn_libvirt_access() diff --git a/test/test_qemu_recover.py b/test/test_qemu_recover.py index a51440b..aff78a0 100644 --- a/test/test_qemu_recover.py +++ b/test/test_qemu_recover.py @@ -143,13 +143,14 @@ class LExtraction(unittest.TestCase): self.todo = TODO.__new__(TODO) self.lances = [] self.todo.execute = mock.MagicMock() - self.todo.execute.exec_command_live.side_effect = ( - lambda cmd, **k: self.lances.append(cmd) or 0 + self.todo.execute.exec_command_live.side_effect = lambda cmd, **k: ( + self.lances.append(cmd) or 0 ) def test_copy_out_names_both_ends(self): - with mock.patch.object(qr.os, "makedirs"), mock.patch( - "builtins.print" + with ( + mock.patch.object(qr.os, "makedirs"), + mock.patch("builtins.print"), ): ok = self.todo._qemu_recover_copy_out( DISQUE, "/dev/sda3", "/home/erplibre", "/tmp/vm-a-backup" @@ -163,9 +164,12 @@ class LExtraction(unittest.TestCase): def test_an_uncreatable_destination_stops_before_running(self): """guestfish s'arrêterait sur une erreur qui ne dit pas laquelle des deux extrémités manque.""" - with mock.patch.object( - qr.os, "makedirs", side_effect=OSError("lecture seule") - ), mock.patch("builtins.print"): + with ( + mock.patch.object( + qr.os, "makedirs", side_effect=OSError("lecture seule") + ), + mock.patch("builtins.print"), + ): ok = self.todo._qemu_recover_copy_out( DISQUE, "/dev/sda3", "/home", "/interdit" ) @@ -178,8 +182,8 @@ class LesDiagnostics(unittest.TestCase): self.todo = TODO.__new__(TODO) self.lances = [] self.todo.execute = mock.MagicMock() - self.todo.execute.exec_command_live.side_effect = ( - lambda cmd, **k: self.lances.append(cmd) or 0 + self.todo.execute.exec_command_live.side_effect = lambda cmd, **k: ( + self.lances.append(cmd) or 0 ) def test_the_four_probes_answer_four_questions(self): @@ -227,17 +231,21 @@ class LeMenu(unittest.TestCase): # Le numéro se DÉDUIT du menu : l'entrée de config est la dernière # des entrées numérotées. L'écrire en dur ferait passer le test au # premier réarrangement de sections, sans rien prouver. - with m.patch("script.todo.qemu_menu.click") as click, m.patch.object( - todo, "_qemu_ensure_tools", return_value=True - ), m.patch("builtins.print"): + with ( + m.patch("script.todo.qemu_menu.click") as click, + m.patch.object(todo, "_qemu_ensure_tools", return_value=True), + m.patch("builtins.print"), + ): click.prompt.side_effect = ["0"] todo.prompt_execute_qemu() aide = click.prompt.call_args[0][0] numeros = re.findall(r"^\[(\d+)\]", aide, re.M) dernier = max(int(n) for n in numeros) - with m.patch("script.todo.qemu_menu.click") as click, m.patch.object( - todo, "_qemu_ensure_tools", return_value=True - ), m.patch("builtins.print"): + with ( + m.patch("script.todo.qemu_menu.click") as click, + m.patch.object(todo, "_qemu_ensure_tools", return_value=True), + m.patch("builtins.print"), + ): click.prompt.side_effect = [str(dernier), "0"] todo.prompt_execute_qemu() self.assertEqual(1, len(lancees), lancees) diff --git a/test/test_qemu_setup_host_reboot.py b/test/test_qemu_setup_host_reboot.py index f3727c0..73dc060 100644 --- a/test/test_qemu_setup_host_reboot.py +++ b/test/test_qemu_setup_host_reboot.py @@ -66,24 +66,21 @@ class SetupHostReboot(unittest.TestCase): questions.append(texte) return reponse - with mock.patch.object(DQ, "ensure_tools"), mock.patch.object( - DQ, "ensure_libvirt_service" - ), mock.patch.object( - DQ, "ensure_libvirt_group", return_value=True - ), mock.patch.object( - DQ, "ensure_ssh_key" - ), mock.patch.object( - DQ, "ensure_network" - ), mock.patch.object( - DQ, "kernel_modules_stale", return_value=NOYAU_PERIME - ), mock.patch.object( - DQ, "libvirt_ready", return_value=True - ), mock.patch.object( - DQ, "network_state", return_value=(False, True) - ), mock.patch.object( - DQ, "schedule_reboot", side_effect=lambda r: reboots.append(r) - ), mock.patch.object( - DQ, "prompt_yes_no", side_effect=question + with ( + mock.patch.object(DQ, "ensure_tools"), + mock.patch.object(DQ, "ensure_libvirt_service"), + mock.patch.object(DQ, "ensure_libvirt_group", return_value=True), + mock.patch.object(DQ, "ensure_ssh_key"), + mock.patch.object(DQ, "ensure_network"), + mock.patch.object( + DQ, "kernel_modules_stale", return_value=NOYAU_PERIME + ), + mock.patch.object(DQ, "libvirt_ready", return_value=True), + mock.patch.object(DQ, "network_state", return_value=(False, True)), + mock.patch.object( + DQ, "schedule_reboot", side_effect=lambda r: reboots.append(r) + ), + mock.patch.object(DQ, "prompt_yes_no", side_effect=question), ): with redirect_stdout(io.StringIO()): try: diff --git a/test/test_qemu_shrink_tools.py b/test/test_qemu_shrink_tools.py index 38c2cbc..c174dd3 100644 --- a/test/test_qemu_shrink_tools.py +++ b/test/test_qemu_shrink_tools.py @@ -25,8 +25,8 @@ from contextlib import redirect_stdout from unittest.mock import patch sys.argv = ["todo.py"] -from script.todo.todo import TODO # noqa: E402 from script.todo import todo_install # noqa: E402 +from script.todo.todo import TODO # noqa: E402 from script.todo.todo_i18n import t # noqa: E402 FAMILIES = ("apt-get", "dnf", "pacman", "zypper") @@ -83,9 +83,11 @@ class ShrinkToolsBase(unittest.TestCase): # `shutil` est UN seul objet module partagé : patcher son « which » # par n'importe quel importateur le patche pour todo_install aussi, # qui est le vrai lecteur du PATH depuis le refactor. - with patch("script.todo.qemu_manage.shutil.which", which), patch( - "builtins.input", demande - ), redirect_stdout(buf): + with ( + patch("script.todo.qemu_manage.shutil.which", which), + patch("builtins.input", demande), + redirect_stdout(buf), + ): left = self.todo._qemu_install_shrink_tools(list(missing)) return self.todo.execute.ran, left, buf.getvalue() @@ -213,9 +215,10 @@ class TestBackupSpace(unittest.TestCase): disque de 60 Go apparents mais 8 Go alloués ne demande que 8 Go.""" faux = os.stat_result((0o644, 0, 0, 1, 0, 0, 60 * self.GIB, 0, 0, 0)) # st_blocks n'est pas dans le tuple : on le pose à part. - with patch("script.todo.qemu_manage.os.stat") as stat, patch( - "script.todo.qemu_manage.shutil.disk_usage" - ) as du: + with ( + patch("script.todo.qemu_manage.os.stat") as stat, + patch("script.todo.qemu_manage.shutil.disk_usage") as du, + ): stat.return_value = type( "S", (), {"st_blocks": 8 * self.GIB // 512} )() @@ -238,11 +241,15 @@ class TestBackupSpace(unittest.TestCase): vu.append(invite) return answer - with patch.object( - TODO, - "_qemu_backup_need_and_free", - staticmethod(lambda d: (besoin, libre)), - ), patch("builtins.input", demande), redirect_stdout(io.StringIO()): + with ( + patch.object( + TODO, + "_qemu_backup_need_and_free", + staticmethod(lambda d: (besoin, libre)), + ), + patch("builtins.input", demande), + redirect_stdout(io.StringIO()), + ): retenu = self.todo._qemu_ask_backup("/x/d.qcow2") return vu[-1], retenu diff --git a/test/test_qemu_sommes.py b/test/test_qemu_sommes.py index b3e3176..832d4c4 100644 --- a/test/test_qemu_sommes.py +++ b/test/test_qemu_sommes.py @@ -214,9 +214,12 @@ class UneImageGardeeQuiAVieilli(unittest.TestCase): Path(dest).write_bytes(pose) sorti = False - with mock.patch.object( - DQ.urllib.request, "urlopen", lambda *a, **k: Reponse() - ), mock.patch.object(DQ, "download_image", faux_telechargement): + with ( + mock.patch.object( + DQ.urllib.request, "urlopen", lambda *a, **k: Reponse() + ), + mock.patch.object(DQ, "download_image", faux_telechargement), + ): try: DQ.verify_sha256(self.URL, image, False, "ubuntu", urls) except SystemExit: diff --git a/test/test_qemu_start_egl.py b/test/test_qemu_start_egl.py index 718dd94..83c8e7d 100644 --- a/test/test_qemu_start_egl.py +++ b/test/test_qemu_start_egl.py @@ -39,8 +39,7 @@ SORTIE_EGL = ( "qemu-system-x86_64: egl: render node init failed" ) SORTIE_AUTRE = ( - "error: Failed to start domain: internal error:" - " qemu unexpectedly closed" + "error: Failed to start domain: internal error: qemu unexpectedly closed" ) ETAT = { @@ -80,8 +79,8 @@ class LeRetrait(unittest.TestCase): self.todo = TODO.__new__(TODO) self.lances = [] self.todo.execute = mock.MagicMock() - self.todo.execute.exec_command_live.side_effect = ( - lambda cmd, **k: self.lances.append(cmd) or 0 + self.todo.execute.exec_command_live.side_effect = lambda cmd, **k: ( + self.lances.append(cmd) or 0 ) self.todo._qemu_dumpxml = lambda n, **k: "" self.todo._qemu_autostart = lambda n: True @@ -208,14 +207,16 @@ class LeDiagnostic(unittest.TestCase): return R() - with mock.patch.object( - __import__("script.todo.qemu_manage", fromlist=["x"]).subprocess, - "run", - side_effect=faux_run, - ), mock.patch.object( - os.path, "expanduser", return_value=tmp - ), mock.patch( - "builtins.print" + with ( + mock.patch.object( + __import__( + "script.todo.qemu_manage", fromlist=["x"] + ).subprocess, + "run", + side_effect=faux_run, + ), + mock.patch.object(os.path, "expanduser", return_value=tmp), + mock.patch("builtins.print"), ): self.todo._qemu_diagnostics() return sorted(os.listdir(tmp)) @@ -317,16 +318,16 @@ class LeDiagnostic(unittest.TestCase): import subprocess as sp with tempfile.TemporaryDirectory() as tmp: - with mock.patch.object( - __import__( - "script.todo.qemu_manage", fromlist=["x"] - ).subprocess, - "run", - side_effect=sp.TimeoutExpired("x", 30), - ), mock.patch.object( - os.path, "expanduser", return_value=tmp - ), mock.patch( - "builtins.print" + with ( + mock.patch.object( + __import__( + "script.todo.qemu_manage", fromlist=["x"] + ).subprocess, + "run", + side_effect=sp.TimeoutExpired("x", 30), + ), + mock.patch.object(os.path, "expanduser", return_value=tmp), + mock.patch("builtins.print"), ): self.todo._qemu_diagnostics() contenu = Path(tmp, os.listdir(tmp)[0]).read_text(encoding="utf-8") @@ -341,8 +342,8 @@ class LesOutilsDuRapport(unittest.TestCase): self.todo = TODO.__new__(TODO) self.lances = [] self.todo.execute = mock.MagicMock() - self.todo.execute.exec_command_live.side_effect = ( - lambda cmd, **k: self.lances.append(cmd) or 0 + self.todo.execute.exec_command_live.side_effect = lambda cmd, **k: ( + self.lances.append(cmd) or 0 ) def _proposer(self, presents, reponse="o"): @@ -358,10 +359,10 @@ class LesOutilsDuRapport(unittest.TestCase): def faux_which(binaire): return "/usr/bin/x" if binaire in connus else None - with mock.patch.object( - qm.shutil, "which", side_effect=faux_which - ), mock.patch("builtins.input", return_value=reponse), mock.patch( - "builtins.print" + with ( + mock.patch.object(qm.shutil, "which", side_effect=faux_which), + mock.patch("builtins.input", return_value=reponse), + mock.patch("builtins.print"), ): return self.todo._qemu_diag_offer_tools() @@ -382,17 +383,21 @@ class LesOutilsDuRapport(unittest.TestCase): droits : la commande entière, sudo compris, précède la question.""" connus = {"pacman"} vus = [] - with mock.patch.object( - qm.shutil, - "which", - side_effect=lambda b: "/usr/bin/x" if b in connus else None, - ), mock.patch( - "builtins.input", - side_effect=lambda p="": (vus.append(("?", p)), "n")[1], - ), mock.patch( - "builtins.print", - side_effect=lambda *a, **k: vus.append( - ("!", " ".join(str(x) for x in a)) + with ( + mock.patch.object( + qm.shutil, + "which", + side_effect=lambda b: "/usr/bin/x" if b in connus else None, + ), + mock.patch( + "builtins.input", + side_effect=lambda p="": (vus.append(("?", p)), "n")[1], + ), + mock.patch( + "builtins.print", + side_effect=lambda *a, **k: vus.append( + ("!", " ".join(str(x) for x in a)) + ), ), ): self.todo._qemu_diag_offer_tools() @@ -409,12 +414,14 @@ class LesOutilsDuRapport(unittest.TestCase): """Un lancement scripté n'a personne pour répondre : l'invite y lève EOFError, et le rapport — déjà écrit — ne doit pas tomber avec.""" connus = {"pacman"} - with mock.patch.object( - qm.shutil, - "which", - side_effect=lambda b: "/usr/bin/x" if b in connus else None, - ), mock.patch("builtins.input", side_effect=EOFError), mock.patch( - "builtins.print" + with ( + mock.patch.object( + qm.shutil, + "which", + side_effect=lambda b: "/usr/bin/x" if b in connus else None, + ), + mock.patch("builtins.input", side_effect=EOFError), + mock.patch("builtins.print"), ): self.assertFalse(self.todo._qemu_diag_offer_tools()) self.assertEqual([], self.lances) @@ -438,14 +445,16 @@ class LeConseilAcl(unittest.TestCase): def _conseil(self, nodes, acl): vus = [] - with mock.patch.object( - self.todo, "_qemu_nvidia_nodes", return_value=nodes - ), mock.patch.object( - self.todo, "_qemu_acl_active", return_value=acl - ), mock.patch( - "builtins.print", - side_effect=lambda *a, **k: vus.append( - " ".join(str(x) for x in a) + with ( + mock.patch.object( + self.todo, "_qemu_nvidia_nodes", return_value=nodes + ), + mock.patch.object(self.todo, "_qemu_acl_active", return_value=acl), + mock.patch( + "builtins.print", + side_effect=lambda *a, **k: vus.append( + " ".join(str(x) for x in a) + ), ), ): parle = self.todo._qemu_nvidia_acl_advice() @@ -550,10 +559,10 @@ class LaSondeVideo(unittest.TestCase): def _sonder(self, device, argv0=None, extra=()): """Sortie de la sonde devant un /proc bâti pour l'occasion.""" - from script.todo.qemu_manage import _DIAG_VIDEO_PY - import subprocess + from script.todo.qemu_manage import _DIAG_VIDEO_PY + with tempfile.TemporaryDirectory() as tmp: proc = Path(tmp) / "proc" / "4242" proc.mkdir(parents=True) @@ -664,16 +673,20 @@ class La3DParVM(unittest.TestCase): } todo = TODO.__new__(TODO) vus = [] - with mock.patch.object( - TODO, "_qemu_list_domains", lambda s: list(faux) - ), mock.patch.object( - TODO, - "_qemu_dumpxml", - staticmethod(lambda n, inactive=True: faux[n]), - ), mock.patch( - "builtins.print", - side_effect=lambda *a, **k: vus.append( - " ".join(str(x) for x in a) + with ( + mock.patch.object( + TODO, "_qemu_list_domains", lambda s: list(faux) + ), + mock.patch.object( + TODO, + "_qemu_dumpxml", + staticmethod(lambda n, inactive=True: faux[n]), + ), + mock.patch( + "builtins.print", + side_effect=lambda *a, **k: vus.append( + " ".join(str(x) for x in a) + ), ), ): todo._qemu_vm_3d_report() diff --git a/test/test_qemu_sudo_reason.py b/test/test_qemu_sudo_reason.py index 0b0dace..33419b3 100644 --- a/test/test_qemu_sudo_reason.py +++ b/test/test_qemu_sudo_reason.py @@ -187,9 +187,12 @@ class LeRenduDuMenu(unittest.TestCase): todo = TODO.__new__(TODO) module = mock.MagicMock() module.sudo_facts.return_value = faits - with mock.patch.object( - TODO, "_qemu_import_module", return_value=module - ), mock.patch.object(os, "geteuid", return_value=euid): + with ( + mock.patch.object( + TODO, "_qemu_import_module", return_value=module + ), + mock.patch.object(os, "geteuid", return_value=euid), + ): return todo._qemu_sudo_lines() def test_root_is_told_nothing(self): @@ -227,9 +230,12 @@ class LeRenduDuMenu(unittest.TestCase): """Le récapitulatif ne doit pas tomber parce qu'un module ne se charge pas : c'est la page qu'on relit avant de créer des disques.""" todo = TODO.__new__(TODO) - with mock.patch.object( - TODO, "_qemu_import_module", side_effect=OSError("absent") - ), mock.patch.object(os, "geteuid", return_value=1000): + with ( + mock.patch.object( + TODO, "_qemu_import_module", side_effect=OSError("absent") + ), + mock.patch.object(os, "geteuid", return_value=1000), + ): self.assertEqual([], todo._qemu_sudo_lines()) def test_every_sentence_is_translated(self): diff --git a/test/test_qemu_tunnel_menu.py b/test/test_qemu_tunnel_menu.py index 1677b6a..3e290bb 100644 --- a/test/test_qemu_tunnel_menu.py +++ b/test/test_qemu_tunnel_menu.py @@ -38,8 +38,9 @@ class _MenuCase(unittest.TestCase): def _play(self, answers): it = iter(answers) buf = io.StringIO() - with mock.patch("builtins.input", lambda *a: next(it)), mock.patch( - "sys.stdout", buf + with ( + mock.patch("builtins.input", lambda *a: next(it)), + mock.patch("sys.stdout", buf), ): self.todo._qemu_tunnel_menu() return buf.getvalue() @@ -168,8 +169,9 @@ class TestVirtViewer(_MenuCase): self._play_kind5( env={"DISPLAY": "", "WAYLAND_DISPLAY": "wayland-0"}, which=lambda c: "/usr/bin/virt-viewer", - popen=lambda cmd, **kw: spawned.setdefault("cmd", cmd) - and mock.Mock(), + popen=lambda cmd, **kw: ( + spawned.setdefault("cmd", cmd) and mock.Mock() + ), ) self.assertIn("virt-viewer", spawned.get("cmd", [])) @@ -198,8 +200,8 @@ class TestEnsureVirtViewer(unittest.TestCase): self.todo = TODO.__new__(TODO) self.ran = [] self.todo.execute = mock.Mock() - self.todo.execute.exec_command_live = ( - lambda cmd, **kw: self.ran.append(cmd) + self.todo.execute.exec_command_live = lambda cmd, **kw: ( + self.ran.append(cmd) ) def test_present_means_nothing_to_do(self): @@ -215,27 +217,32 @@ class TestEnsureVirtViewer(unittest.TestCase): return seen["virt-viewer"].pop(0) return "/usr/bin/dnf" if cmd == "dnf" else None - with mock.patch("shutil.which", which), mock.patch( - "sys.stdout", io.StringIO() + with ( + mock.patch("shutil.which", which), + mock.patch("sys.stdout", io.StringIO()), ): self.assertTrue(self.todo._qemu_ensure_virt_viewer()) self.assertEqual(1, len(self.ran)) self.assertIn("dnf install -y virt-viewer", self.ran[0]) def test_no_manager_is_said_not_guessed(self): - with mock.patch("shutil.which", lambda c: None), mock.patch( - "sys.stdout", io.StringIO() - ) as out: + with ( + mock.patch("shutil.which", lambda c: None), + mock.patch("sys.stdout", io.StringIO()) as out, + ): self.assertFalse(self.todo._qemu_ensure_virt_viewer()) self.assertIn("paquets", out.getvalue().lower() + "paquets") self.assertEqual([], self.ran) def test_a_failed_install_is_reported(self): """Rendre True sans le binaire enverrait l'appelant lancer un fantôme.""" - with mock.patch( - "shutil.which", - lambda c: "/usr/bin/apt-get" if c == "apt-get" else None, - ), mock.patch("sys.stdout", io.StringIO()): + with ( + mock.patch( + "shutil.which", + lambda c: "/usr/bin/apt-get" if c == "apt-get" else None, + ), + mock.patch("sys.stdout", io.StringIO()), + ): self.assertFalse(self.todo._qemu_ensure_virt_viewer()) self.assertEqual(1, len(self.ran)) diff --git a/test/test_qemu_vm_name.py b/test/test_qemu_vm_name.py index 5d53468..f8230ae 100644 --- a/test/test_qemu_vm_name.py +++ b/test/test_qemu_vm_name.py @@ -82,9 +82,10 @@ class TestNameRoundTrip(unittest.TestCase): def test_rolling_release_resolves_back_to_latest(self): todo = TODO() - with patch.object( - TODO, "_qemu_vm_arch", lambda self, name: "amd64" - ), patch.object(TODO, "_native_arch", staticmethod(lambda: "amd64")): + with ( + patch.object(TODO, "_qemu_vm_arch", lambda self, name: "amd64"), + patch.object(TODO, "_native_arch", staticmethod(lambda: "amd64")), + ): class Catalogue: DISTROS = {"arch": (["latest"], "latest")} diff --git a/test/test_qemu_vm_table.py b/test/test_qemu_vm_table.py index b479f19..375a8e8 100644 --- a/test/test_qemu_vm_table.py +++ b/test/test_qemu_vm_table.py @@ -14,8 +14,8 @@ deux VM réelles, et la largeur de la ligne — un tableau qui déborde de 80 colonnes se replie et devient illisible. """ -import io import contextlib +import io import subprocess import sys import unittest diff --git a/test/test_todo.py b/test/test_todo.py index 65e8ef4..58bf753 100644 --- a/test/test_todo.py +++ b/test/test_todo.py @@ -118,14 +118,19 @@ class TestGetOdooVersion(unittest.TestCase): with open(odoo_version_file, "w") as f: f.write("18.0") - with patch( - "script.todo.version_manager.VERSION_DATA_FILE", version_file - ), patch( - "script.todo.version_manager.INSTALLED_ODOO_VERSION_FILE", - os.path.join(tmpdir, "nonexistent.txt"), - ), patch( - "script.todo.version_manager.ODOO_VERSION_FILE", - odoo_version_file, + with ( + patch( + "script.todo.version_manager.VERSION_DATA_FILE", + version_file, + ), + patch( + "script.todo.version_manager.INSTALLED_ODOO_VERSION_FILE", + os.path.join(tmpdir, "nonexistent.txt"), + ), + patch( + "script.todo.version_manager.ODOO_VERSION_FILE", + odoo_version_file, + ), ): versions, installed, odoo_current = get_odoo_version() @@ -154,14 +159,19 @@ class TestGetOdooVersion(unittest.TestCase): with open(installed_file, "w") as f: f.write("odoo18.0\nodoo16.0\n") - with patch( - "script.todo.version_manager.VERSION_DATA_FILE", version_file - ), patch( - "script.todo.version_manager.INSTALLED_ODOO_VERSION_FILE", - installed_file, - ), patch( - "script.todo.version_manager.ODOO_VERSION_FILE", - os.path.join(tmpdir, "nonexistent"), + with ( + patch( + "script.todo.version_manager.VERSION_DATA_FILE", + version_file, + ), + patch( + "script.todo.version_manager.INSTALLED_ODOO_VERSION_FILE", + installed_file, + ), + patch( + "script.todo.version_manager.ODOO_VERSION_FILE", + os.path.join(tmpdir, "nonexistent"), + ), ): versions, installed, odoo_current = get_odoo_version() @@ -387,12 +397,11 @@ class TestTestMenuDispatch(unittest.TestCase): def _choose(self, entry): todo = TODO() - with patch.object( - todo, "execute_unit_tests" - ) as mock_run, patch.object(todo, "execute_test_module"), patch( - "click.prompt", side_effect=[entry, "0"] - ), patch( - "builtins.print" + with ( + patch.object(todo, "execute_unit_tests") as mock_run, + patch.object(todo, "execute_test_module"), + patch("click.prompt", side_effect=[entry, "0"]), + patch("builtins.print"), ): todo.prompt_execute_test() return mock_run @@ -457,12 +466,12 @@ class TestSetupClaudeCommit(unittest.TestCase): def test_existing_file_and_refusal_writes_nothing(self): todo = TODO() - with patch("os.path.exists", return_value=True), patch( - "builtins.input", return_value="n" - ), patch("builtins.open") as mock_open, patch( - "os.makedirs" - ) as mock_makedirs, patch( - "builtins.print" + with ( + patch("os.path.exists", return_value=True), + patch("builtins.input", return_value="n"), + patch("builtins.open") as mock_open, + patch("os.makedirs") as mock_makedirs, + patch("builtins.print"), ): todo._setup_claude_command( "commit", "template_claude_commands_commit.md" @@ -477,12 +486,12 @@ class TestSetupClaudeCommit(unittest.TestCase): """Le pendant : sans lui, la méthode pourrait ne JAMAIS écrire et le test ci-dessus resterait vert.""" todo = TODO() - with patch("os.path.exists", return_value=True), patch( - "builtins.input", return_value="y" - ), patch("builtins.open", mock_open(read_data="gabarit")), patch( - "os.makedirs" - ) as mock_makedirs, patch( - "builtins.print" + with ( + patch("os.path.exists", return_value=True), + patch("builtins.input", return_value="y"), + patch("builtins.open", mock_open(read_data="gabarit")), + patch("os.makedirs") as mock_makedirs, + patch("builtins.print"), ): todo._setup_claude_command( "commit", "template_claude_commands_commit.md" @@ -571,8 +580,9 @@ class TestClaudePlugins(unittest.TestCase): def test_absent_binary_reports_without_executing(self): todo = TODO() todo.execute = MagicMock() - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "builtins.print" + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("builtins.print"), ): self.assertEqual(todo._claude_plugin_exec("list"), 1) self.assertEqual( @@ -614,22 +624,25 @@ class TestClaudePlugins(unittest.TestCase): def test_refusing_the_preferred_list_installs_nothing(self): todo = TODO() - with patch("builtins.input", return_value="n"), patch.object( - todo, "_claude_plugin_exec" - ) as mock_exec, patch("builtins.print"): + with ( + patch("builtins.input", return_value="n"), + patch.object(todo, "_claude_plugin_exec") as mock_exec, + patch("builtins.print"), + ): todo._claude_install_preferred_plugins() mock_exec.assert_not_called() def test_accepting_installs_only_what_is_missing(self): todo = TODO() - with patch("builtins.input", return_value="y"), patch.object( - todo, "_claude_plugin_exec" - ) as mock_exec, patch.object( - todo, - "_claude_plugin_is_installed", - side_effect=lambda name: name == "pyright-lsp", - ), patch( - "builtins.print" + with ( + patch("builtins.input", return_value="y"), + patch.object(todo, "_claude_plugin_exec") as mock_exec, + patch.object( + todo, + "_claude_plugin_is_installed", + side_effect=lambda name: name == "pyright-lsp", + ), + patch("builtins.print"), ): todo._claude_install_preferred_plugins() called = [call.args[0] for call in mock_exec.call_args_list] @@ -648,7 +661,7 @@ class TestClaudePlugins(unittest.TestCase): todo = TODO() with tempfile.TemporaryDirectory() as tmp: for name, body in ( - ("good", '{"plugins":[{"name":"a",' '"description":"d"}]}'), + ("good", '{"plugins":[{"name":"a","description":"d"}]}'), ("broken", "{not json"), ): folder = os.path.join(tmp, name, ".claude-plugin") @@ -674,11 +687,13 @@ class TestClaudePlugins(unittest.TestCase): ("pyright-lsp", "official", "Python language server"), ("mongodb", "official", "Document database"), ] - with patch.object( - todo, "_claude_marketplace_catalog", return_value=catalog - ), patch("builtins.input", return_value="python"), patch( - "builtins.print" - ) as mock_print: + with ( + patch.object( + todo, "_claude_marketplace_catalog", return_value=catalog + ), + patch("builtins.input", return_value="python"), + patch("builtins.print") as mock_print, + ): todo._claude_plugin_search() printed = " ".join( str(call.args[0]) for call in mock_print.call_args_list diff --git a/test/test_todo_i18n.py b/test/test_todo_i18n.py index f51ad26..97cd670 100644 --- a/test/test_todo_i18n.py +++ b/test/test_todo_i18n.py @@ -103,20 +103,26 @@ class TestGetLang(unittest.TestCase): os.unlink(f.name) def test_env_variable_fallback(self): - with patch.object( - todo_i18n, - "ENV_VAR_FILE", - "/nonexistent/path", - ), patch.dict(os.environ, {"EL_LANG": "en"}): + with ( + patch.object( + todo_i18n, + "ENV_VAR_FILE", + "/nonexistent/path", + ), + patch.dict(os.environ, {"EL_LANG": "en"}), + ): result = todo_i18n.get_lang() self.assertEqual(result, "en") def test_default_is_fr(self): - with patch.object( - todo_i18n, - "ENV_VAR_FILE", - "/nonexistent/path", - ), patch.dict(os.environ, {}, clear=True): + with ( + patch.object( + todo_i18n, + "ENV_VAR_FILE", + "/nonexistent/path", + ), + patch.dict(os.environ, {}, clear=True), + ): result = todo_i18n.get_lang() self.assertEqual(result, "fr") @@ -127,9 +133,10 @@ class TestGetLang(unittest.TestCase): f.write('EL_LANG="de"\n') f.flush() try: - with patch.object( - todo_i18n, "ENV_VAR_FILE", f.name - ), patch.dict(os.environ, {}, clear=True): + with ( + patch.object(todo_i18n, "ENV_VAR_FILE", f.name), + patch.dict(os.environ, {}, clear=True), + ): result = todo_i18n.get_lang() self.assertEqual(result, "fr") finally: diff --git a/test/test_todo_install.py b/test/test_todo_install.py index 8ec04e4..3f3b95b 100644 --- a/test/test_todo_install.py +++ b/test/test_todo_install.py @@ -43,30 +43,35 @@ class TestFamilyDetection(unittest.TestCase): def test_the_distribution_id_decides_before_the_path(self): """Une machine peut porter deux gestionnaires ; l'ID dit lequel possède le système.""" - with patch( - "builtins.open", mock_open(read_data='ID="debian"\n') - ), patch( - "script.todo.todo_install.shutil.which", _only("apt-get", "dnf") + with ( + patch("builtins.open", mock_open(read_data='ID="debian"\n')), + patch( + "script.todo.todo_install.shutil.which", + _only("apt-get", "dnf"), + ), ): self.assertEqual(todo_install.family(), "apt-get") def test_the_path_decides_when_the_id_is_unknown(self): - with patch( - "builtins.open", mock_open(read_data='ID="nonesuch"\n') - ), patch("script.todo.todo_install.shutil.which", _only("zypper")): + with ( + patch("builtins.open", mock_open(read_data='ID="nonesuch"\n')), + patch("script.todo.todo_install.shutil.which", _only("zypper")), + ): self.assertEqual(todo_install.family(), "zypper") def test_an_id_whose_manager_is_absent_falls_back(self): """Un conteneur Debian minimal sans apt-get ne doit pas mener à une commande apt-get qui n'existe pas.""" - with patch( - "builtins.open", mock_open(read_data='ID="debian"\n') - ), patch("script.todo.todo_install.shutil.which", _only("dnf")): + with ( + patch("builtins.open", mock_open(read_data='ID="debian"\n')), + patch("script.todo.todo_install.shutil.which", _only("dnf")), + ): self.assertEqual(todo_install.family(), "dnf") def test_no_manager_is_none_not_a_guess(self): - with patch("builtins.open", mock_open(read_data="")), patch( - "script.todo.todo_install.shutil.which", _only() + with ( + patch("builtins.open", mock_open(read_data="")), + patch("script.todo.todo_install.shutil.which", _only()), ): self.assertIsNone(todo_install.family()) self.assertIsNone(todo_install.install_command(["p"])) @@ -84,11 +89,12 @@ class TestFamilyDetection(unittest.TestCase): ("opensuse-tumbleweed", "zypper"), ("arch", "pacman"), ): - with patch( - "builtins.open", mock_open(read_data=f'ID="{os_id}"\n') - ), patch( - "script.todo.todo_install.shutil.which", - _only(*todo_install.FAMILIES), + with ( + patch("builtins.open", mock_open(read_data=f'ID="{os_id}"\n')), + patch( + "script.todo.todo_install.shutil.which", + _only(*todo_install.FAMILIES), + ), ): self.assertEqual(todo_install.family(), attendu, os_id) diff --git a/test/test_todo_longtest.py b/test/test_todo_longtest.py index 7cbc0f3..5ec86b9 100644 --- a/test/test_todo_longtest.py +++ b/test/test_todo_longtest.py @@ -1223,11 +1223,14 @@ class TestLeMenuDesDeuxTests(unittest.TestCase): vrai = builtins.input self.addCleanup(setattr, builtins, "input", vrai) demande = [] - self.todo._pve_host = lambda ask=True: demande.append(ask) or { - "target": "root@10.0.0.5", - "jump": "", - "version": "9.2.11", - } + self.todo._pve_host = lambda ask=True: ( + demande.append(ask) + or { + "target": "root@10.0.0.5", + "jump": "", + "version": "9.2.11", + } + ) builtins.input = lambda _p="": "2" with contextlib.redirect_stdout(io.StringIO()) as sortie: args = self.todo._longtest_depart("deep_proxmox.py") @@ -1702,10 +1705,8 @@ class TestLeDecompteDeLaDestruction(unittest.TestCase): """Elles survivaient aux machines : des entrées mortes dont le ProxyJump désigne un hôte qui n'existe plus.""" retires = [] - moteur.retirer_alias = ( - lambda rapport, journal=None, nom_base="": retires.append( - [e.get("alias") for e in rapport["etages"]] - ) + moteur.retirer_alias = lambda rapport, journal=None, nom_base="": ( + retires.append([e.get("alias") for e in rapport["etages"]]) ) self._lancer(etage1_ok=True) self.assertEqual(len(retires), 1) diff --git a/test/test_todo_rtk.py b/test/test_todo_rtk.py index 56b6486..e8853fd 100644 --- a/test/test_todo_rtk.py +++ b/test/test_todo_rtk.py @@ -37,14 +37,16 @@ class TestRtkLocate(unittest.TestCase): self.assertEqual(TODO().rtk_locate(), ("/usr/bin/rtk", True)) def test_found_outside_path(self): - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=True + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=True), ): self.assertEqual(TODO().rtk_locate(), (FALLBACK, False)) def test_absent(self): - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=False + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=False), ): self.assertEqual(TODO().rtk_locate(), (None, False)) @@ -56,8 +58,9 @@ class TestRtkExec(unittest.TestCase): todo = TODO() todo.execute = MagicMock() todo.execute.exec_command_live.return_value = 0 - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=True + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=True), ): todo.rtk_exec("gain") command = todo.execute.exec_command_live.call_args[0][0] @@ -67,8 +70,9 @@ class TestRtkExec(unittest.TestCase): def test_absent_runs_nothing(self): todo = TODO() todo.execute = MagicMock() - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=False + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=False), ): with redirect_stdout(io.StringIO()): status = todo.rtk_exec("gain") @@ -110,8 +114,9 @@ class TestRtkReportInstall(unittest.TestCase): todo = TODO() todo.execute = MagicMock() todo.execute.exec_command_live.return_value = (0, ["rtk 0.47.0"]) - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=True + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=True), ): output = self.report(todo, 0) self.assertIn("✅", output) @@ -120,8 +125,9 @@ class TestRtkReportInstall(unittest.TestCase): def test_success_without_binary_is_not_a_success(self): todo = TODO() todo.execute = MagicMock() - with patch("script.todo.todo.shutil.which", return_value=None), patch( - "script.todo.todo.os.access", return_value=False + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch("script.todo.todo.os.access", return_value=False), ): output = self.report(todo, 0) self.assertIn("❌", output) diff --git a/test/test_todo_shell_tools.py b/test/test_todo_shell_tools.py index 5c856de..1333c94 100644 --- a/test/test_todo_shell_tools.py +++ b/test/test_todo_shell_tools.py @@ -87,9 +87,11 @@ class TestRcTarget(ShellFixture): def choisit(self, shell_env="/bin/bash", reponse=None): entree = refuse_input if reponse is None else (lambda *a: reponse) - with patch.dict(os.environ, {"SHELL": shell_env}), patch( - "builtins.input", entree - ), redirect_stdout(io.StringIO()): + with ( + patch.dict(os.environ, {"SHELL": shell_env}), + patch("builtins.input", entree), + redirect_stdout(io.StringIO()), + ): return self.todo._shell_rc_target() def test_no_file_means_bash_without_asking(self): @@ -191,9 +193,11 @@ class TestHookStarship(ShellFixture): def hook(self, shell_env="/bin/bash"): out = io.StringIO() - with patch.dict(os.environ, {"SHELL": shell_env}), patch( - "builtins.input", refuse_input - ), redirect_stdout(out): + with ( + patch.dict(os.environ, {"SHELL": shell_env}), + patch("builtins.input", refuse_input), + redirect_stdout(out), + ): self.todo._shell_hook_starship() return out.getvalue() @@ -246,11 +250,14 @@ class TestInstallStarship(unittest.TestCase): def test_an_installed_binary_goes_straight_to_the_shell(self): todo = TODO() todo.execute = MagicMock() - with patch( - "script.todo.todo.shutil.which", return_value="/usr/bin/starship" - ), patch.object(TODO, "_shell_hook_starship") as hook, patch.object( - TODO, "_shell_install_starship_binary" - ) as poser: + with ( + patch( + "script.todo.todo.shutil.which", + return_value="/usr/bin/starship", + ), + patch.object(TODO, "_shell_hook_starship") as hook, + patch.object(TODO, "_shell_install_starship_binary") as poser, + ): todo._shell_install_starship() poser.assert_not_called() hook.assert_called_once() @@ -259,12 +266,11 @@ class TestInstallStarship(unittest.TestCase): todo = TODO() todo.execute = MagicMock() out = io.StringIO() - with patch( - "script.todo.todo.shutil.which", return_value=None - ), patch.object(TODO, "_shell_hook_starship") as hook, patch.object( - TODO, "_shell_install_starship_binary" - ), redirect_stdout( - out + with ( + patch("script.todo.todo.shutil.which", return_value=None), + patch.object(TODO, "_shell_hook_starship") as hook, + patch.object(TODO, "_shell_install_starship_binary"), + redirect_stdout(out), ): todo._shell_install_starship() hook.assert_not_called() @@ -275,12 +281,15 @@ class TestInstallStarship(unittest.TestCase): todo = TODO() todo.execute = MagicMock() appels = [] - with patch( - "script.todo.todo.todo_install.install_command", - return_value=["sudo", "pacman", "-S", "starship"], - ), patch( - "script.todo.todo.todo_install.ask_and_install", - side_effect=lambda *a, **k: appels.append(a[1]) or None, + with ( + patch( + "script.todo.todo.todo_install.install_command", + return_value=["sudo", "pacman", "-S", "starship"], + ), + patch( + "script.todo.todo.todo_install.ask_and_install", + side_effect=lambda *a, **k: appels.append(a[1]) or None, + ), ): todo._shell_install_starship_binary() self.assertEqual(len(appels), 1) @@ -289,13 +298,16 @@ class TestInstallStarship(unittest.TestCase): todo = TODO() todo.execute = MagicMock() appels = [] - with patch( - "script.todo.todo.todo_install.install_command", return_value=None - ), patch( - "script.todo.todo.todo_install.ask_and_install", - side_effect=lambda *a, **k: appels.append(a[1]) or 0, - ), redirect_stdout( - io.StringIO() + with ( + patch( + "script.todo.todo.todo_install.install_command", + return_value=None, + ), + patch( + "script.todo.todo.todo_install.ask_and_install", + side_effect=lambda *a, **k: appels.append(a[1]) or 0, + ), + redirect_stdout(io.StringIO()), ): todo._shell_install_starship_binary() self.assertEqual(appels, [TODO._STARSHIP_UPSTREAM]) @@ -333,9 +345,11 @@ class TestUpstreamTools(ShellFixture): def installe(self, outil, status=0): self.todo.execute.exec_command_live.return_value = status out = io.StringIO() - with patch.dict(os.environ, {"SHELL": "/bin/bash"}), patch( - "builtins.input", refuse_input - ), redirect_stdout(out): + with ( + patch.dict(os.environ, {"SHELL": "/bin/bash"}), + patch("builtins.input", refuse_input), + redirect_stdout(out), + ): self.todo._shell_install_upstream_tool(outil) return out.getvalue() diff --git a/test/test_todo_sshfs.py b/test/test_todo_sshfs.py index 5f82296..1270ef7 100644 --- a/test/test_todo_sshfs.py +++ b/test/test_todo_sshfs.py @@ -309,15 +309,14 @@ class TestFlux(unittest.TestCase): return vrai_rmdir(path) out = io.StringIO() - with mock.patch( - "builtins.input", lambda *a: next(reponses, "") - ), mock.patch( - "os.path.expanduser", - lambda p: chemin if p.endswith("config") else p, - ), mock.patch( - "os.makedirs", makedirs - ), mock.patch( - "os.rmdir", rmdir + with ( + mock.patch("builtins.input", lambda *a: next(reponses, "")), + mock.patch( + "os.path.expanduser", + lambda p: chemin if p.endswith("config") else p, + ), + mock.patch("os.makedirs", makedirs), + mock.patch("os.rmdir", rmdir), ): with contextlib.redirect_stdout(out): todo._configure_sshfs() diff --git a/test/test_todo_upgrade_capture.py b/test/test_todo_upgrade_capture.py index 65cc94d..6b25d3c 100644 --- a/test/test_todo_upgrade_capture.py +++ b/test/test_todo_upgrade_capture.py @@ -320,10 +320,8 @@ class TestTheDriverUsesIt(unittest.TestCase): def test_the_full_screen_path_still_captures_nothing(self): # C'est sa raison d'être : un plein écran derrière un tube # renonce et retombe sur son rapport texte. - import inspect - import ast - + import inspect import textwrap arbre = ast.parse( diff --git a/test/test_transform_external.py b/test/test_transform_external.py index 3aa186c..fd2c0e8 100644 --- a/test/test_transform_external.py +++ b/test/test_transform_external.py @@ -2553,7 +2553,7 @@ class TestRapportDeLEnTete(unittest.TestCase): "acai,830,2019-01-03,B2\n" "adobe,940,2019-01-04,C3\n" ) - SANS = "ZK204817,501,100.5\n" "ZK204818,502,101.5\n" "ZK204819,503,102.5\n" + SANS = "ZK204817,501,100.5\nZK204818,502,101.5\nZK204819,503,102.5\n" def test_l_empan_est_une_LISTE_serialisable(self): """Un set ne passe pas `json.dump`, et ce rapport traverse un @@ -6545,18 +6545,19 @@ def _injecter_parties_de_copie(chemin): parties = { "xl/pivotCache/pivotCacheRecords1.xml": ( '' - f"" + f'' "" ), "xl/externalLinks/externalLink1.xml": ( '' - f"" + f'' "" ), } - with zipfile.ZipFile(chemin) as entree, zipfile.ZipFile( - temporaire, "w", zipfile.ZIP_DEFLATED - ) as sortie: + with ( + zipfile.ZipFile(chemin) as entree, + zipfile.ZipFile(temporaire, "w", zipfile.ZIP_DEFLATED) as sortie, + ): for item in entree.infolist(): # Les membres NON XML passent en octets : décoder # xl/media/image1.png lèverait UnicodeDecodeError. @@ -6581,9 +6582,10 @@ def _injecter_cache(chemin): + "" ) injecte = False - with zipfile.ZipFile(chemin) as entree, zipfile.ZipFile( - temporaire, "w", zipfile.ZIP_DEFLATED - ) as sortie: + with ( + zipfile.ZipFile(chemin) as entree, + zipfile.ZipFile(temporaire, "w", zipfile.ZIP_DEFLATED) as sortie, + ): for item in entree.infolist(): octets = entree.read(item.filename) # Le classeur porte plusieurs graphiques : viser le PREMIER qui @@ -7124,8 +7126,8 @@ class TestLaBandeDuCalibreNeSElargitPas(unittest.TestCase): valeur = 10 ** (largeur - 1) + 7 vues = [] rng = random.Random(0) - rng.randint = ( - lambda bas, haut: vues.append((bas, haut)) or haut + rng.randint = lambda bas, haut: ( + vues.append((bas, haut)) or haut ) sortie = noyau.nouveau_nombre( valeur, diff --git a/test/test_uninstall_addons_theme.py b/test/test_uninstall_addons_theme.py index b95778f..c48855e 100755 --- a/test/test_uninstall_addons_theme.py +++ b/test/test_uninstall_addons_theme.py @@ -260,8 +260,8 @@ class TestKeepOrDeleteTheLeftovers(unittest.TestCase): self.deleted = [] self.original_backup = theme_leftover.backup_attachments self.original_delete = theme_leftover.delete_attachments - theme_leftover.backup_attachments = ( - lambda db, th, rows, fs=None: self.saved.append(rows) or ["/tmp/x"] + theme_leftover.backup_attachments = lambda db, th, rows, fs=None: ( + self.saved.append(rows) or ["/tmp/x"] ) theme_leftover.delete_attachments = ( lambda db, rows, cfg="./config.conf": ( @@ -421,8 +421,8 @@ class TestTheIdentifiersSentToOdoo(unittest.TestCase): # autour d'un identifiant et Odoo refuse tout le lot. pushed = {} original = theme_leftover.subprocess.run - theme_leftover.subprocess.run = ( - lambda *a, **kw: pushed.update(script=kw.get("input", "")) + theme_leftover.subprocess.run = lambda *a, **kw: ( + pushed.update(script=kw.get("input", "")) or type("R", (), {"returncode": 0, "stdout": "", "stderr": ""})() ) self.addCleanup(setattr, theme_leftover.subprocess, "run", original) diff --git a/test/test_version.py b/test/test_version.py index 567d86f..af219c2 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -7,19 +7,19 @@ import os import sys import tempfile import unittest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from script.version.update_env_version import ( - Update, - remove_dot_path, - die, - ERPLIBRE_TEMPLATE_VERSION, - VENV_TEMPLATE_FILE, - MANIFEST_TEMPLATE_FILE, - PYPROJECT_TEMPLATE_FILE, - POETRY_LOCK_TEMPLATE_FILE, ADDONS_TEMPLATE_FILE, + ERPLIBRE_TEMPLATE_VERSION, + MANIFEST_TEMPLATE_FILE, ODOO_TEMPLATE_FILE, + POETRY_LOCK_TEMPLATE_FILE, + PYPROJECT_TEMPLATE_FILE, + VENV_TEMPLATE_FILE, + Update, + die, + remove_dot_path, ) @@ -204,18 +204,23 @@ class TestUpdateDetectVersion(unittest.TestCase): f.write("18.0") with open(poetry_file, "w") as f: f.write("2.1.3") - with patch( - "script.version.update_env_version.VERSION_PYTHON_FILE", - py_file, - ), patch( - "script.version.update_env_version.VERSION_ODOO_FILE", - odoo_file, - ), patch( - "script.version.update_env_version.VERSION_POETRY_FILE", - poetry_file, - ), patch( - "script.version.update_env_version.INSTALLED_ODOO_VERSION_FILE", - os.path.join(tmpdir, "nonexist"), + with ( + patch( + "script.version.update_env_version.VERSION_PYTHON_FILE", + py_file, + ), + patch( + "script.version.update_env_version.VERSION_ODOO_FILE", + odoo_file, + ), + patch( + "script.version.update_env_version.VERSION_POETRY_FILE", + poetry_file, + ), + patch( + "script.version.update_env_version.INSTALLED_ODOO_VERSION_FILE", + os.path.join(tmpdir, "nonexist"), + ), ): result = update.detect_version() self.assertTrue(result) diff --git a/test/test_vpn_menu.py b/test/test_vpn_menu.py index 61dbc3c..88d433c 100644 --- a/test/test_vpn_menu.py +++ b/test/test_vpn_menu.py @@ -584,7 +584,7 @@ class SecretsOnlyWhenThereAreSome(MenuBase): typed.clear() with patch( "getpass.getpass", - lambda prompt="": (typed.append(prompt) or ""), + lambda prompt="": typed.append(prompt) or "", ): with redirect_stdout(io.StringIO()): self.todo._vpn_store_secrets() diff --git a/test/test_vpn_presets.py b/test/test_vpn_presets.py index 0524cf1..25d66e1 100644 --- a/test/test_vpn_presets.py +++ b/test/test_vpn_presets.py @@ -626,7 +626,7 @@ class DelegatedSso(unittest.TestCase): OpenconnectDriver, "ensure_ready", return_value=True ): with patch( - "script.vpn.drivers.openconnect." "wait_for_interface_address", + "script.vpn.drivers.openconnect.wait_for_interface_address", side_effect=slowly, ) as waited: with redirect_stdout(io.StringIO()): @@ -649,7 +649,7 @@ class DelegatedSso(unittest.TestCase): OpenconnectDriver, "ensure_ready", return_value=True ): with patch( - "script.vpn.drivers.openconnect." "wait_for_interface_address", + "script.vpn.drivers.openconnect.wait_for_interface_address", return_value=[], ): with patch( diff --git a/test/test_vpn_render.py b/test/test_vpn_render.py index f49d3d4..95c301e 100644 --- a/test/test_vpn_render.py +++ b/test/test_vpn_render.py @@ -139,29 +139,23 @@ class RenderedFiles(unittest.TestCase): driver = _driver() runner = Runner(dry_run=False, quiet=True) module = "script.vpn.drivers.l2tp_ipsec" - with patch( - f"{module}.netlink_family_available", return_value=True - ), patch.object( - runner, "cmd", return_value=(0, "established successfully") - ), patch.object( - runner, "write", return_value=0 - ), patch.object( - runner, "block", return_value=False - ), patch.object( - runner, "mkdir", return_value=0 - ), patch( - "script.vpn.drivers.base.locate", return_value="/usr/bin/x" - ), patch( - f"{module}.locate", return_value="" - ), patch( - f"{module}.resolve", return_value="203.0.113.9" - ), patch( - f"{module}.ppp_interfaces", return_value=set() - ), patch( - f"{module}.wait_for_new_interface", return_value="ppp0" - ), patch( - f"{module}.wait_for_interface_address", return_value=[] - ) as attente: + with ( + patch(f"{module}.netlink_family_available", return_value=True), + patch.object( + runner, "cmd", return_value=(0, "established successfully") + ), + patch.object(runner, "write", return_value=0), + patch.object(runner, "block", return_value=False), + patch.object(runner, "mkdir", return_value=0), + patch("script.vpn.drivers.base.locate", return_value="/usr/bin/x"), + patch(f"{module}.locate", return_value=""), + patch(f"{module}.resolve", return_value="203.0.113.9"), + patch(f"{module}.ppp_interfaces", return_value=set()), + patch(f"{module}.wait_for_new_interface", return_value="ppp0"), + patch( + f"{module}.wait_for_interface_address", return_value=[] + ) as attente, + ): self.assertFalse(driver.up(runner)) attente.assert_called_once() self.assertTrue( @@ -392,18 +386,20 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): return runner TENU = ( - "UNCONN 0 0 0.0.0.0:1701 0.0.0.0:*" - ' users:(("xl2tpd",pid=12314,fd=3))' + 'UNCONN 0 0 0.0.0.0:1701 0.0.0.0:* users:(("xl2tpd",pid=12314,fd=3))' ) def test_it_offers_to_stop_xl2tpd_then_carries_on(self): driver = _driver() # `ss` dit « tenu », puis « libre » après le correctif. runner = self._runner([self.TENU, ""]) - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="o" + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o"), ): self.assertTrue(driver._l2tp_port_is_free(runner)) self.assertFalse(runner.failures) @@ -411,10 +407,13 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): def test_a_refused_fix_leaves_the_failure_standing(self): driver = _driver() runner = self._runner([self.TENU]) - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="n" + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="n"), ): self.assertFalse(driver._l2tp_port_is_free(runner)) self.assertTrue(runner.failures) @@ -424,10 +423,13 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): c'est réglé.""" driver = _driver() runner = self._runner([self.TENU, self.TENU]) - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="o" + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o"), ): self.assertFalse(driver._l2tp_port_is_free(runner)) self.assertTrue(runner.failures) @@ -453,10 +455,13 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): return 0, "" runner.cmd = cmd - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="o" + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o"), ): self.assertTrue(driver._l2tp_port_is_free(runner)) self.assertFalse(runner.failures) @@ -480,9 +485,13 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): return 0, "" runner.cmd = cmd - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("builtins.input") as demande: + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("builtins.input") as demande, + ): self.assertFalse(driver._l2tp_port_is_free(runner)) demande.assert_not_called() self.assertTrue( @@ -499,11 +508,14 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): ' users:(("un-autre-truc",pid=999,fd=3))' ) runner = self._runner([autre]) - with patch( - "script.vpn.drivers.l2tp_ipsec.locate", return_value="/usr/bin/ss" - ), patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="o" - ) as demande: + with ( + patch( + "script.vpn.drivers.l2tp_ipsec.locate", + return_value="/usr/bin/ss", + ), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o") as demande, + ): self.assertFalse(driver._l2tp_port_is_free(runner)) demande.assert_not_called() self.assertTrue(runner.failures) @@ -517,9 +529,11 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): runner = Runner(dry_run=False, quiet=True) runner.cmd = lambda *a, **k: (0, "") sortie = io.StringIO() - with patch("sys.stdin.isatty", return_value=True), patch( - "builtins.input", return_value="o" - ) as demande, redirect_stdout(sortie): + with ( + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o") as demande, + redirect_stdout(sortie), + ): runner.propose("essai", "systemctl stop x", question="Arrêter ?") # Rien ne doit être confié au prompt d'`input` : c'est lui qui ne # porte pas de fin de ligne. @@ -531,7 +545,7 @@ class WhenTheToolKnowsTheFixItOffersIt(unittest.TestCase): serait pire que le problème qu'il résout.""" runner = Runner(dry_run=False, quiet=True) applique = [] - runner.cmd = lambda *a, **k: (applique.append(a) or (0, "")) + runner.cmd = lambda *a, **k: applique.append(a) or (0, "") with patch("sys.stdin.isatty", return_value=False): self.assertFalse( runner.propose("essai", "systemctl stop quelque-chose") @@ -787,15 +801,18 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): def test_a_kernel_without_any_module_tree_is_not_stale(self): """Un noyau compilé sans modules n'a rien à redémarrer : le déclarer périmé enverrait redémarrer pour rien.""" - with patch("os.path.isdir", return_value=False), patch( - "os.listdir", return_value=[] + with ( + patch("os.path.isdir", return_value=False), + patch("os.listdir", return_value=[]), ): self.assertEqual(base.stale_kernel(), "") def test_the_running_tree_gone_while_another_stands_is_stale(self): - with patch("os.path.isdir", return_value=False), patch( - "os.listdir", return_value=["1.2.3-neuf"] - ), patch("platform.release", return_value="1.2.2-vieux"): + with ( + patch("os.path.isdir", return_value=False), + patch("os.listdir", return_value=["1.2.3-neuf"]), + patch("platform.release", return_value="1.2.2-vieux"), + ): self.assertEqual(base.stale_kernel(), "1.2.2-vieux") def test_a_present_tree_is_never_stale(self): @@ -849,9 +866,11 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): runner.cmd = lambda label, command, **k: ( lancees.append(command) or (0, "") ) - with self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), patch( - "sys.stdin.isatty", return_value=True - ), patch("builtins.input", return_value="o"): + with ( + self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o"), + ): self.assertTrue(driver.propose_reboot(runner)) self.assertEqual(lancees, ["systemctl reboot"]) @@ -862,8 +881,9 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): runner.cmd = lambda label, command, **k: ( lancees.append(command) or (0, "") ) - with self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), patch( - "sys.stdin.isatty", return_value=False + with ( + self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), + patch("sys.stdin.isatty", return_value=False), ): self.assertFalse(driver.propose_reboot(runner)) self.assertEqual(lancees, []) @@ -871,9 +891,10 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): def test_nothing_reboots_on_a_dry_run(self): driver = _driver() runner = _dry_runner(driver) - with self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), patch( - "builtins.input" - ) as demande: + with ( + self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), + patch("builtins.input") as demande, + ): self.assertFalse(driver.propose_reboot(runner)) demande.assert_not_called() @@ -883,9 +904,11 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): driver = _driver() runner = Runner(dry_run=False, quiet=True) runner.cmd = lambda label, command, **k: (0, "") - with self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), patch( - "sys.stdin.isatty", return_value=True - ), patch("builtins.input", return_value="o"): + with ( + self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), + patch("sys.stdin.isatty", return_value=True), + patch("builtins.input", return_value="o"), + ): self.assertFalse(driver.ensure_ready(runner)) self.assertTrue(runner.failures) @@ -895,8 +918,9 @@ class WhatTheKernelGivesAndWhatOnlyARebootGivesBack(unittest.TestCase): configuration de quelqu'un a été touchée pour rien.""" driver = _driver() runner = Runner(dry_run=False, quiet=True) - with self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), patch( - "sys.stdin.isatty", return_value=False + with ( + self._kernel(driver, (self.ABSENT,), "1.2.2-vieux"), + patch("sys.stdin.isatty", return_value=False), ): self.assertFalse(driver.up(runner)) touches = [