Merge branch 'dev' into bullseye

This commit is contained in:
Alexandre Aubin 2021-10-02 19:59:09 +02:00
commit 8ef882b4f5
47 changed files with 453 additions and 272 deletions

29
.github/workflows/i18n.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Autoreformat locale files
on:
push:
branches:
- dev
jobs:
i18n:
name: Autoreformat locale files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Apply reformating scripts
id: action_reformat
run: |
python3 test/remove_stale_i18n_strings.py
python3 test/autofix_locale_format.py
python3 test/reformat_locales.py
git diff -w --exit-code
- name: Create Pull Request
if: ${{ failure() }}
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: "Reformat locale files"
commit-message: ":robot: Reformat locale files"
body: |
Automatic pull request using the scripts in `test/`
base: ${{ github.head_ref }}
branch: actions/i18nreformat

View file

@ -45,3 +45,5 @@ jobs:
pip install tox tox-gh-actions
- name: Linter
run: tox -e py39-invalidcode
- name: Mypy
run: tox -e py39-mypy

View file

@ -2,6 +2,7 @@
<div align="center">
![Version](https://img.shields.io/github/v/tag/yunohost/moulinette?label=version&sort=semver)
[![Tests status](https://github.com/YunoHost/moulinette/actions/workflows/tox.yml/badge.svg)](https://github.com/YunoHost/moulinette/actions/workflows/tox.yml)
[![GitHub license](https://img.shields.io/github/license/YunoHost/moulinette)](https://github.com/YunoHost/moulinette/blob/dev/LICENSE)

23
debian/changelog vendored
View file

@ -4,6 +4,29 @@ moulinette (11.0.0~alpha) unstable; urgency=low
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 05 Feb 2021 00:02:38 +0100
moulinette (4.3.1) testing; urgency=low
- [mod] Rework cli prompt mecanisc ([#303](https://github.com/YunoHost/moulinette/pull/303))
- [i18n] Translations updated for Indonesian, Russian, Turkish
Thanks to all contributors <3 ! (Éric Gaspar, liimee)
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 29 Sep 2021 22:37:28 +0200
moulinette (4.3.0) testing; urgency=low
- [enh] Allow file type in actionmaps ([#258](https://github.com/YunoHost/moulinette/pull/258))
- [refactor] Rework and externalize the authenticator system ([#270](https://github.com/YunoHost/moulinette/pull/270))
- [security] Add httponly to API cookies (8562c05d)
- [enh] Add prefill and multiline in prompt ([#290](https://github.com/YunoHost/moulinette/pull/290), 08f7866f)
- [enh] Support bytes/stream in write_to_file (6e714314)
- [fix] Various technical bugs in utils/process.py (fdc61c91, 4eb60dac, 3741101d)
- [i18n] Translations updated for French, Galician, Persian, Ukrainian
Thanks to all contributors <3 ! (Éric Gaspar, José M, Kay0u, ljf, Parviz Homayun, ppr, Tymofii-Lytvynenko)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 19 Sep 2021 21:19:43 +0200
moulinette (4.2.4) stable; urgency=low
- [fix] Avoid warning and use safeloader ([#281](https://github.com/YunoHost/moulinette/pull/281))

4
debian/control vendored
View file

@ -14,7 +14,9 @@ Depends: ${misc:Depends}, ${python3:Depends},
python3-gevent-websocket,
python3-toml,
python3-psutil,
python3-tz
python3-tz,
python3-prompt-toolkit,
python3-pygments
Breaks: yunohost (<< 4.1)
Description: prototype interfaces with ease in Python
Quickly and easily prototype interfaces for your application.

View file

@ -1,8 +1,6 @@
{
"argument_required": "المُعامِل '{argument}' مطلوب",
"authentication_required": "المصادقة مطلوبة",
"authentication_required_long": "المصادقة مطلوبة قبل القيام بهذا الإجراء",
"colon": "{}: ",
"confirm": "تأكيد {prompt}",
"deprecated_command": "'{prog} {command}' تم التخلي عنه و سوف تتم إزالته مستقبلا",
"deprecated_command_alias": "'{prog} {old}' تم التخلي عنه و سوف يتم إزالته مستقبلا، إستخدم '{prog} {new}' بدلا من ذلك",
@ -11,10 +9,7 @@
"folder_exists": "إنّ المجلد موجود من قبل : '{path}'",
"instance_already_running": "هناك بالفعل عملية YunoHost جارية. الرجاء الانتظار حتى ينتهي الأمر قبل تشغيل آخر.",
"invalid_argument": "المُعامِل غير صالح '{argument}': {error}",
"invalid_password": "كلمة السر خاطئة",
"invalid_usage": "إستعمال غير صالح، إستخدم --help لعرض المساعدة",
"ldap_attribute_already_exists": "الخاصية '{attribute}' موجودة مسبقا و تحمل القيمة '{value}'",
"ldap_server_down": "لا يمكن الإتصال بخادم LDAP",
"logged_in": "مُتّصل",
"logged_out": "تم تسجيل خروجك",
"not_logged_in": "لم تقم بعدُ بتسجيل دخولك",
@ -25,7 +20,6 @@
"server_already_running": "هناك خادم يشتغل على ذاك المنفذ",
"success": "تم بنجاح !",
"unable_authenticate": "تعذرت المصادقة",
"unable_retrieve_session": "تعذرت مواصلة الجلسة بسبب '{exception}'",
"unknown_group": "الفريق '{group}' مجهول",
"unknown_user": "المستخدم '{user}' مجهول",
"values_mismatch": "القيمتين غير متطابقتين",
@ -47,8 +41,5 @@
"info": "معلومة:",
"warn_the_user_about_waiting_lock_again": "جارٍ الانتظار…",
"warn_the_user_that_lock_is_acquired": "لقد انتهى تنفيذ ذاك الأمر للتوّ ، جارٍ تنفيذ هذا الأمر",
"warn_the_user_about_waiting_lock": "هناك أمر لـ YunoHost قيد التشغيل حاليا. في انتظار انتهاء تنفيذه قبل تشغيل التالي",
"ldap_server_is_down_restart_it": "إنّ خدمة LDAP غير مشغّلة ، نحن بصدد محاولة إعادة تشغيلها…",
"session_expired": "لقد انتهت مدة صلاحية الجلسة. رجاءً أعد الإستيثاق.",
"invalid_token": "إنّ الرمز المميز غير صالح - يرجى الإستيثاق"
"warn_the_user_about_waiting_lock": "هناك أمر لـ YunoHost قيد التشغيل حاليا. في انتظار انتهاء تنفيذه قبل تشغيل التالي"
}

View file

@ -1,8 +1,6 @@
{
"argument_required": "Es requereix l'argument {argument}",
"authentication_required": "Es requereix autenticació",
"authentication_required_long": "Es requereix autenticació per realitzar aquesta tasca",
"colon": "{}: ",
"confirm": "Confirmar{prompt}",
"deprecated_command": "{prog}{command}és obsolet i es desinstal·larà en el futur",
"deprecated_command_alias": "{prog}{old}és obsolet i es desinstal·larà en el futur, utilitzeu {prog}{new}en el seu lloc",
@ -11,10 +9,7 @@
"folder_exists": "La carpeta ja existeix: '{path}'",
"instance_already_running": "Ja hi ha una operació de YunoHost en curs. Espereu a que s'acabi abans d'executar-ne una altra.",
"invalid_argument": "Argument invàlid '{argument}': {error}",
"invalid_password": "Contrasenya invàlida",
"invalid_usage": "Utilització invàlida, utilitzeu --help per veure l'ajuda",
"ldap_attribute_already_exists": "L'atribut '{attribute}' ja existeix amb el valor '{value}'",
"ldap_server_down": "No s'ha pogut connectar amb el servidor LDAP",
"logged_in": "Sessió iniciada",
"logged_out": "Sessió tancada",
"not_logged_in": "No ha iniciat sessió",
@ -25,7 +20,6 @@
"server_already_running": "Ja s'està executant un servidor en aquest port",
"success": "Èxit!",
"unable_authenticate": "No s'ha pogut autenticar",
"unable_retrieve_session": "No s'ha pogut recuperar la sessió a causa de «{exception}»",
"unknown_group": "Grup '{group}' desconegut",
"unknown_user": "Usuari '{user}' desconegut",
"values_mismatch": "Els valors no coincideixen",
@ -48,8 +42,5 @@
"corrupted_toml": "El fitxer TOML ha estat corromput en la lectura des de {ressource} (motiu: {error})",
"warn_the_user_about_waiting_lock": "Hi ha una altra ordre de YunoHost en execució, s'executarà aquesta ordre un cop l'anterior hagi acabat",
"warn_the_user_about_waiting_lock_again": "Encara en espera…",
"warn_the_user_that_lock_is_acquired": "L'altra ordre tot just ha acabat, ara s'executarà aquesta ordre",
"invalid_token": "Testimoni no vàlid - torneu-vos a autenticar",
"ldap_server_is_down_restart_it": "El servei LDAP està caigut, s'està intentant tornar-lo a engegar…",
"session_expired": "La sessió a expirat. Torneu-vos a autenticar."
"warn_the_user_that_lock_is_acquired": "L'altra ordre tot just ha acabat, ara s'executarà aquesta ordre"
}

View file

@ -1 +1 @@
{}
{}

View file

@ -1,8 +1,6 @@
{
"argument_required": "参数“{argument}”是必须的",
"authentication_required": "需要验证",
"authentication_required_long": "此操作需要验证",
"colon": "{} ",
"confirm": "确认 {prompt}",
"deprecated_command": "{prog}{command}已经放弃使用,将来会删除",
"deprecated_command_alias": "{prog}{old}已经放弃使用,将来会删除,请使用{prog}{new}代替",
@ -12,10 +10,7 @@
"info": "信息:",
"instance_already_running": "已经有一个YunoHost操作正在运行。 请等待它完成再运行另一个。",
"invalid_argument": "参数错误{argument}{error}",
"invalid_password": "密码错误",
"invalid_usage": "用法错误,输入 --help 查看帮助信息",
"ldap_attribute_already_exists": "参数{attribute}已赋值{value}",
"ldap_server_down": "无法连接LDAP服务器",
"logged_in": "登录",
"logged_out": "登出",
"not_logged_in": "您未登录",
@ -26,7 +21,6 @@
"server_already_running": "服务已运行在指定端口",
"success": "成功!",
"unable_authenticate": "认证失败",
"unable_retrieve_session": "由于“ {exception}”,无法检索会话",
"unknown_group": "未知组{group}",
"unknown_user": "未知用户{user}",
"values_mismatch": "值不匹配",
@ -48,8 +42,5 @@
"warn_the_user_that_lock_is_acquired": "另一个命令刚刚完成,现在启动此命令",
"warn_the_user_about_waiting_lock_again": "还在等...",
"warn_the_user_about_waiting_lock": "目前正在运行另一个YunoHost命令我们在运行此命令之前等待它完成",
"corrupted_toml": "从{ressources}读取的TOML损坏原因{errors}",
"invalid_token": "令牌无效-请进行身份验证",
"ldap_server_is_down_restart_it": "LDAP服务已下线正在尝试重启服务……",
"session_expired": "会话已过期。请重新进行身份验证。"
}
"corrupted_toml": "从{ressources}读取的TOML损坏原因{errors}"
}

View file

@ -1,7 +1,6 @@
{
"password": "Heslo",
"logged_out": "Jste odhlášen/a",
"ldap_server_is_down_restart_it": "LDAP služba neběží, probíhá pokus o její nastartování...",
"warn_the_user_that_lock_is_acquired": "Předchozí operace dokončena, nyní spouštíme tuto",
"warn_the_user_about_waiting_lock_again": "Stále čekáme...",
"warn_the_user_about_waiting_lock": "Jiná YunoHost operace právě probíhá, před spuštěním této čekáme na její dokončení",
@ -24,8 +23,6 @@
"values_mismatch": "Hodnoty nesouhlasí",
"unknown_user": "Neznámý '{user}' uživatel",
"unknown_group": "Neznámá '{group}' skupina",
"session_expired": "Sezení vypršelo. Přihlašte se znovu, prosím.",
"unable_retrieve_session": "Není možné obdržet sezení neboť '{exception}'",
"unable_authenticate": "Není možné ověřit",
"success": "Zadařilo se!",
"server_already_running": "Na tomto portu je server již provozován",
@ -34,11 +31,7 @@
"operation_interrupted": "Operace přerušena",
"not_logged_in": "Nejste přihlášen",
"logged_in": "Přihlášení",
"ldap_server_down": "Spojení s LDAP serverem se nezdařilo",
"ldap_attribute_already_exists": "Atribut '{attribute}' již obsahuje hodnotu '{value}'",
"invalid_usage": "Nesprávné použití, pass --help pro zobrazení nápovědy",
"invalid_token": "Nesprávný token - ověřte se prosím",
"invalid_password": "Nesprávné heslo",
"invalid_argument": "Nesprávný argument '{argument}': {error}",
"instance_already_running": "Právě probíhá jiná YunoHost operace. Před spuštěním další operace vyčkejte na její dokončení.",
"info": "Info:",
@ -48,8 +41,6 @@
"deprecated_command_alias": "'{prog} {old}' je zastaralý a bude odebrán v budoucích verzích, použijte '{prog} {new}'",
"deprecated_command": "'{prog} {command}' je zastaralý a bude odebrán v budoucích verzích",
"confirm": "Potvrdit {prompt}",
"colon": "{}: ",
"authentication_required_long": "K provedení této akce je vyžadováno ověření",
"authentication_required": "Vyžadováno ověření",
"argument_required": "Je vyžadován argument '{argument}'"
}

View file

@ -1,18 +1,13 @@
{
"argument_required": "Der Parameter {argument} ist erforderlich",
"authentication_required": "Anmeldung erforderlich",
"authentication_required_long": "Bitte erst anmelden um diese Aktion auszuführen",
"colon": "{}: ",
"confirm": "Bestätige {prompt}",
"error": "Fehler:",
"file_not_exist": "Datei ist nicht vorhanden: '{path}'",
"folder_exists": "Ordner existiert bereits: '{path}'",
"instance_already_running": "Es läuft bereits eine YunoHost-Operation. Bitte warte, bis sie fertig ist, bevor du eine weitere startest.",
"invalid_argument": "Argument ungültig '{argument}': {error}",
"invalid_password": "Passwort falsch",
"invalid_usage": "Falscher Aufruf, verwende --help für den Hilfstext",
"ldap_attribute_already_exists": "Attribute existieren bereits: '{attribute}={value}'",
"ldap_server_down": "LDAP-Server nicht erreichbar",
"logged_in": "Angemeldet",
"logged_out": "Abgemeldet",
"not_logged_in": "Du bist nicht angemeldet",
@ -23,7 +18,6 @@
"server_already_running": "Einen anderer Dienst arbeitet bereits auf diesem Port",
"success": "Erfolg!",
"unable_authenticate": "Anmelden fehlgeschlagen",
"unable_retrieve_session": "Sitzung konnte nicht abgerufen werden. Grund: '{exception}'",
"values_mismatch": "Die Werte passen nicht zusammen",
"warning": "Warnung:",
"websocket_request_expected": "Eine WebSocket-Anfrage wurde erwartet",
@ -32,7 +26,6 @@
"unknown_group": "Gruppe '{group}' ist unbekannt",
"unknown_user": "Benutzer '{user}' ist unbekannt",
"info": "Info:",
"invalid_token": "Ungültiger Token - bitte authentifizieren",
"corrupted_json": "Beschädigtes JSON gelesen von {ressource} (reason: {error})",
"unknown_error_reading_file": "Unbekannter Fehler beim Lesen der Datei {file} (reason: {error})",
"cannot_write_file": "Kann Datei {file} nicht schreiben (reason: {error})",
@ -49,7 +42,5 @@
"error_changing_file_permissions": "Fehler beim Ändern der Berechtigungen für {path}: {error}",
"error_removing": "Fehler beim Entfernen {path}: {error}",
"error_writing_file": "Fehler beim Schreiben von Datei {file}: {error}",
"corrupted_toml": "Beschädigtes TOML gelesen von {ressource} (reason: {error})",
"ldap_server_is_down_restart_it": "Der LDAP-Dienst wurde angehalten. Es wird versucht, ihn erneut zu starten...",
"session_expired": "Die Sitzung ist abgelaufen. Bitte authentifizieren Sie sich neu ."
"corrupted_toml": "Beschädigtes TOML gelesen von {ressource} (reason: {error})"
}

View file

@ -1,10 +1,10 @@
{
"argument_required": "Argument '{argument}' is required",
"authentication_required": "Authentication required",
"colon": "{}: ",
"confirm": "Confirm {prompt}",
"deprecated_command": "'{prog} {command}' is deprecated and will be removed in the future",
"deprecated_command_alias": "'{prog} {old}' is deprecated and will be removed in the future, use '{prog} {new}' instead",
"edit_text_question": "{}. Edit this text ? [yN]: ",
"error": "Error:",
"file_not_exist": "File does not exist: '{path}'",
"folder_exists": "Folder already exists: '{path}'",

View file

@ -1,6 +1,5 @@
{
"password": "Pasvorto",
"colon": "{}: ",
"warn_the_user_that_lock_is_acquired": "La alia komando ĵus kompletigis, nun komencante ĉi tiun komandon",
"warn_the_user_about_waiting_lock_again": "Ankoraŭ atendanta...",
"warn_the_user_about_waiting_lock": "Alia komando de YunoHost funkcias ĝuste nun, ni atendas, ke ĝi finiĝos antaŭ ol funkcii ĉi tiu",
@ -23,7 +22,6 @@
"values_mismatch": "Valoroj ne kongruas",
"unknown_user": "Nekonata uzanto '{user}'",
"unknown_group": "Nekonata grupo \"{group}\"",
"unable_retrieve_session": "Ne eblas retrovi la sesion ĉar '{exception}'",
"unable_authenticate": "Ne eblas aŭtentiĝi",
"success": "Sukceson!",
"server_already_running": "Servilo jam funkcias sur tiu haveno",
@ -32,10 +30,7 @@
"operation_interrupted": "Operacio interrompita",
"not_logged_in": "Vi ne estas ensalutinta",
"logged_in": "Ensalutinta",
"ldap_server_down": "Ne eblas atingi la servilon LDAP",
"ldap_attribute_already_exists": "Atributo '{attribute}' jam ekzistas kun valoro '{value}'",
"invalid_usage": "Nevalida uzado, preterpase '--help' por vidi helpon",
"invalid_password": "Nevalida pasvorto",
"invalid_argument": "Nevalida argumento '{argument}': {error}",
"instance_already_running": "Jam funkcias YunoHost-operacio. Bonvolu atendi, ke ĝi finiĝos antaŭ ol funkcii alia.",
"info": "informoj:",
@ -45,11 +40,7 @@
"deprecated_command_alias": "'{prog} {old}' malakceptas kaj estos forigita estonte, uzu anstataŭe '{prog} {new}'",
"deprecated_command": "'{prog} {command}' malakceptas kaj estos forigita estonte",
"confirm": "Konfirmu {prompt}",
"authentication_required_long": "Aŭtentigo necesas por plenumi ĉi tiun agon",
"authentication_required": "Aŭtentigo bezonata",
"argument_required": "Argumento '{argument}' estas bezonata",
"logged_out": "Ensalutinta",
"invalid_token": "Nevalida tokeno - bonvolu autentiki",
"ldap_server_is_down_restart_it": "La LDAP-servo malpliiĝas, provu rekomenci ĝin...",
"session_expired": "La sesio eksvalidiĝis. Bonvolu re-aŭtentikigi."
"logged_out": "Ensalutinta"
}

View file

@ -1,8 +1,6 @@
{
"argument_required": "Se requiere el argumento «{argument}»",
"authentication_required": "Se requiere autentificación",
"authentication_required_long": "Debe autentificarse para realizar esta acción",
"colon": "{}: ",
"confirm": "Confirmar {prompt}",
"deprecated_command": "«{prog} {command}» está obsoleto y será eliminado en el futuro",
"deprecated_command_alias": "«{prog} {old}» está obsoleto y se eliminará en el futuro, use «{prog} {new}» en su lugar",
@ -11,10 +9,7 @@
"folder_exists": "El directorio ya existe: «{path}»",
"instance_already_running": "Ya se está ejecutando una instancia de YunoHost. Espere a que termine antes de ejecutar otra.",
"invalid_argument": "Argumento no válido «{argument}»: {error}",
"invalid_password": "Contraseña no válida",
"invalid_usage": "Uso no válido, utilice --help para ver la ayuda",
"ldap_attribute_already_exists": "El atributo «{attribute}» ya existe con el valor «{value}»",
"ldap_server_down": "No se pudo conectar con el servidor LDAP",
"logged_in": "Sesión iniciada",
"logged_out": "Sesión cerrada",
"not_logged_in": "No ha iniciado sesión",
@ -25,7 +20,6 @@
"server_already_running": "Ya se está ejecutando un servidor en ese puerto",
"success": "¡Éxito!",
"unable_authenticate": "No se puede autentificar",
"unable_retrieve_session": "No se puede recuperar la sesión por «{exception}»",
"unknown_group": "Grupo «{group}» desconocido",
"unknown_user": "Usuario «{user}» desconocido",
"values_mismatch": "Los valores no coinciden",
@ -48,8 +42,5 @@
"corrupted_toml": "Lectura corrupta de TOML desde {ressource} (motivo: {error})",
"warn_the_user_that_lock_is_acquired": "La otra orden recién terminó, iniciando esta orden ahora",
"warn_the_user_about_waiting_lock_again": "Aún esperando...",
"warn_the_user_about_waiting_lock": "Otra orden de YunoHost se está ejecutando ahora, estamos esperando a que termine antes de ejecutar esta",
"invalid_token": "Token invalido - vuelva a autenticarte",
"ldap_server_is_down_restart_it": "El servicio LDAP está caído, intentando reiniciarlo...",
"session_expired": "La sesión expiró. Por favor autenticarse de nuevo."
"warn_the_user_about_waiting_lock": "Otra orden de YunoHost se está ejecutando ahora, estamos esperando a que termine antes de ejecutar esta"
}

View file

@ -1,6 +1,5 @@
{
"argument_required": "'{argument}' argumentua beharrezkoa da",
"logged_out": "Saioa amaitu",
"password": "Pasahitza",
"colon": "{}: "
"password": "Pasahitza"
}

View file

@ -6,11 +6,10 @@
"info": "اطلاعات:",
"folder_exists": "پوشه موجود است: '{path}'",
"file_not_exist": "فایل وجود ندارد: '{path}'",
"error": "خطا:",
"error": "ایراد:",
"deprecated_command_alias": "'{prog} {old}' منسوخ شده است و در آینده حذف خواهد شد ، بجای آن از '{prog} {new}' استفاده کنید",
"deprecated_command": "'{prog} {command}' منسوخ شده است و در آینده حذف خواهد شد",
"confirm": "تأیید {prompt}",
"colon": "{}: ",
"confirm": "تایید کردن {prompt}",
"authentication_required": "احراز هویّت الزامی است",
"argument_required": "استدلال '{argument}' ضروری است",
"password": "کلمه عبور",
@ -41,7 +40,7 @@
"server_already_running": "در حال حاضر یک سرور روی آن پورت کار می کند",
"root_required": "برای انجام این عمل باید کاربر ریشه باشید",
"pattern_not_match": "با الگو مطابقت ندارد",
"operation_interrupted": "عملیات قطع شد",
"operation_interrupted": "عملیات قطع شده است",
"not_logged_in": "‌شما وارد نشده اید",
"logged_out": "خارج شده"
}
}

View file

@ -1,55 +1,47 @@
{
"argument_required": "Largument '{argument}' est requis",
"argument_required": "L'argument '{argument}' est requis",
"authentication_required": "Authentification requise",
"authentication_required_long": "Lauthentification est requise pour exécuter cette action",
"colon": "{} : ",
"confirm": "Confirmez {prompt}",
"deprecated_command": "'{prog} {command}' est déprécié et sera bientôt supprimé",
"deprecated_command_alias": "'{prog} {old}' est déprécié et sera bientôt supprimé, utilisez '{prog} {new}' à la place",
"error": "Erreur :",
"file_not_exist": "Le fichier '{path}' nexiste pas",
"file_not_exist": "Le fichier '{path}' n'existe pas",
"folder_exists": "Le dossier existe déjà : '{path}'",
"instance_already_running": "Une instance est déjà en cours dexécution, merci d'attendre sa fin avant d'en lancer une autre.",
"instance_already_running": "Une instance est déjà en cours d'exécution, merci d'attendre sa fin avant d'en lancer une autre.",
"invalid_argument": "Argument '{argument}' incorrect : {error}",
"invalid_password": "Mot de passe incorrect",
"invalid_usage": "Utilisation erronée, utilisez --help pour accéder à laide",
"ldap_attribute_already_exists": "Lattribut '{attribute}' existe déjà avec la valeur suivante : '{value}'",
"ldap_server_down": "Impossible datteindre le serveur LDAP",
"invalid_usage": "Utilisation erronée, utilisez --help pour accéder à l'aide",
"logged_in": "Connecté",
"logged_out": "Déconnecté",
"not_logged_in": "Vous nêtes pas connecté",
"not_logged_in": "Vous n'êtes pas connecté",
"operation_interrupted": "Opération interrompue",
"password": "Mot de passe",
"pattern_not_match": "Ne correspond pas au motif",
"root_required": "Vous devez être super-utilisateur pour exécuter cette action",
"server_already_running": "Un serveur est déjà en cours dexécution sur ce port",
"server_already_running": "Un serveur est déjà en cours d'exécution sur ce port",
"success": "Succès !",
"unable_authenticate": "Impossible de vous authentifier",
"unable_retrieve_session": "Impossible de récupérer la session à cause de '{exception}'",
"unknown_group": "Le groupe « '{group}' » est inconnu",
"unknown_user": "L'utilisateur « {user} » est inconnu",
"unknown_group": "Le groupe '{group}' est inconnu",
"unknown_user": "L'utilisateur '{user}' est inconnu",
"values_mismatch": "Les valeurs ne correspondent pas",
"warning": "Attention :",
"websocket_request_expected": "Une requête WebSocket est attendue",
"cannot_open_file": "Impossible douvrir le fichier {file} (raison : {error})",
"cannot_open_file": "Impossible d'ouvrir le fichier {file} (raison : {error})",
"cannot_write_file": "Ne peut pas écrire le fichier {file} (raison : {error})",
"unknown_error_reading_file": "Erreur inconnue en essayant de lire le fichier {file} (cause:{error})",
"unknown_error_reading_file": "Erreur inconnue en essayant de lire le fichier {file} (raison :{error})",
"corrupted_json": "Fichier JSON corrompu en lecture depuis {ressource} (raison : {error})",
"error_writing_file": "Erreur en écrivant le fichier {file} : {error}",
"error_removing": "Erreur lors de la suppression {path} : {error}",
"error_changing_file_permissions": "Erreur lors de la modification des autorisations pour {path} : {error}",
"invalid_url": "Impossible de se connecter à {url} ... peut-être que le service est hors service/indisponible/interrompu, ou que vous n'êtes pas correctement connecté à Internet en IPv4/IPv6.",
"invalid_url": "Impossible de se connecter à {url}... peut-être que le service est hors service/indisponible/interrompu, ou que vous n'êtes pas correctement connecté à Internet en IPv4/IPv6.",
"download_ssl_error": "Erreur SSL lors de la connexion à {url}",
"download_timeout": "{url} a pris trop de temps pour répondre : abandon.",
"download_unknown_error": "Erreur lors du téléchargement des données à partir de {url} : {error}",
"download_bad_status_code": "{url} renvoie le code d'état {code}",
"corrupted_yaml": "Fichier YAML corrompu en lecture depuis {ressource} (raison : {error})",
"info": "Info :",
"corrupted_toml": "Fichier TOML corrompu en lecture depuis {ressource} (cause : {error})",
"corrupted_toml": "Fichier TOML corrompu en lecture depuis {ressource} (raison : {error})",
"warn_the_user_about_waiting_lock": "Une autre commande YunoHost est actuellement en cours, nous attendons qu'elle se termine avant de démarrer celle là",
"warn_the_user_about_waiting_lock_again": "Toujours en attente...",
"warn_the_user_that_lock_is_acquired": "La commande précédente vient de se terminer, lancement de cette nouvelle commande",
"invalid_token": "Jeton non valide - veuillez vous authentifier",
"ldap_server_is_down_restart_it": "Le service LDAP s'est arrêté, une tentative de redémarrage est en cours...",
"session_expired": "La session a expiré. Merci de vous ré-authentifier."
}
"edit_text_question": "{}. Modifier ce texte ? [yN] : "
}

View file

@ -1,8 +1,5 @@
{
"ldap_attribute_already_exists": "O atributo '{attribute}' xa existe e ten o valor '{value}'",
"invalid_usage": "Uso non válido, pass --help para ver a axuda",
"invalid_token": "Token non válido - por favor autentícate",
"invalid_password": "Contrasinal non válido",
"invalid_argument": "Argumento non válido '{argument}': {error}",
"instance_already_running": "Hai unha operación de YunoHost en execución. Por favor agarda a que remate antes de realizar unha nova.",
"info": "Info:",
@ -12,8 +9,6 @@
"deprecated_command_alias": "'{prog} {old}' xa non se utiliza e será eliminado no futuro, usa '{prog} {new}' no seu lugar",
"deprecated_command": "'{prog} {command}' xa non se utiliza e xa non se usará no futuro",
"confirm": "Confirma {prompt}",
"colon": "{}: ",
"authentication_required_long": "Requírese autenticación para realizar esta acción",
"authentication_required": "Autenticación requerida",
"argument_required": "O argumento '{argument}' é requerido",
"logged_out": "Sesión pechada",
@ -22,8 +17,6 @@
"values_mismatch": "Non concordan os valores",
"unknown_user": "Usuaria '{user}' descoñecida",
"unknown_group": "Grupo '{group}' descoñecido",
"session_expired": "A sesión caducou. Volve a conectar por favor.",
"unable_retrieve_session": "Non se puido obter a sesión porque '{exception}'",
"unable_authenticate": "Non se puido autenticar",
"success": "Ben feito!",
"server_already_running": "Xa hai un servidor a funcionar nese porto",
@ -32,8 +25,6 @@
"operation_interrupted": "Interrumpeuse a operación",
"not_logged_in": "Non estás conectada",
"logged_in": "Conectada",
"ldap_server_down": "Non se puido acadar o servidor LDAP",
"ldap_server_is_down_restart_it": "O servizo LDAP está caído, intentando reinicialo...",
"warn_the_user_that_lock_is_acquired": "O outro comando rematou, agora executarase este",
"warn_the_user_about_waiting_lock_again": "Agardando...",
"warn_the_user_about_waiting_lock": "Estase executando outro comando de YunoHost neste intre, estamos agardando a que remate para executar este",
@ -51,5 +42,6 @@
"unknown_error_reading_file": "Erro descoñecido ao intentar ler o ficheiro {file} (razón: {error})",
"cannot_write_file": "Non se puido escribir o ficheiro {file} (razón: {error})",
"cannot_open_file": "Non se puido abrir o ficheiro {file} (razón: {error})",
"websocket_request_expected": "Agardábase unha solicitude WebSocket"
}
"websocket_request_expected": "Agardábase unha solicitude WebSocket",
"edit_text_question": "{}. Editar este texto ? [yN]: "
}

View file

@ -1,8 +1,6 @@
{
"argument_required": "तर्क '{argument}' आवश्यक है",
"authentication_required": "प्रमाणीकरण आवश्यक",
"authentication_required_long": "इस कार्य को करने के लिए प्रमाणीकरण आवश्यक है",
"colon": "{}: ",
"confirm": "पुष्टि करें {prompt}",
"deprecated_command": "'{prog}' '{command}' का प्रयोग न करे, भविष्य में इसे हटा दिया जाएगा",
"deprecated_command_alias": "'{prog} {old}' अब पुराना हो गया है और इसे भविष्य में हटा दिया जाएगा, इस की जगह '{prog} {new}' का प्रयोग करें",
@ -11,10 +9,7 @@
"folder_exists": "फ़ोल्डर में पहले से ही मौजूद है: '{path}'",
"instance_already_running": "यूनोहोस्ट का एक कार्य पहले से चल रहा है। कृपया इस कार्य के समाप्त होने का इंतज़ार करें।",
"invalid_argument": "अवैध तर्क '{argument}':'{error}'",
"invalid_password": "अवैध पासवर्ड",
"invalid_usage": "अवैध उपयोग, सहायता देखने के लिए --help साथ लिखे।",
"ldap_attribute_already_exists": "'{attribute}' तर्क पहले इस वैल्यू '{value}' से मौजूद है।",
"ldap_server_down": "LDAP सर्वर तक पहुंचने में असमर्थ।",
"logged_in": "लोग्ड इन",
"logged_out": "लॉग आउट",
"not_logged_in": "आप लोग्ड इन नहीं हैं।",
@ -25,7 +20,6 @@
"server_already_running": "कोई सर्वर पहले से ही इस पोर्ट पर चल रहा है।",
"success": "सफलता!",
"unable_authenticate": "प्रमाणित करने में असमर्थ।",
"unable_retrieve_session": "सेशन को प्राप्त करने में असमर्थ।",
"unknown_group": "अज्ञात ग्रुप: '{group}'",
"unknown_user": "अज्ञात उपयोगकर्ता: '{user}'",
"values_mismatch": "वैल्यूज मेल नहीं खाती।",

View file

@ -11,7 +11,6 @@
"success": "Siker!",
"values_mismatch": "Eltérő értékek",
"warning": "Figyelem:",
"invalid_password": "Helytelen jelszó",
"info": "Információ:",
"file_not_exist": "A fájl nem létezik: '{path}'",
"error": "Hiba:"

19
locales/id.json Normal file
View file

@ -0,0 +1,19 @@
{
"argument_required": "Argumen '{argument}' dibutuhkan",
"authentication_required": "Otentikasi dibutuhkan",
"deprecated_command": "'{prog} {command}' sudah usang dan akan dihapus nanti",
"logged_out": "Berhasil keluar",
"password": "Kata sandi",
"deprecated_command_alias": "'{prog} {old}' sudah usang dan akan dihapus nanti, gunakan '{prog} {new}' saja",
"info": "Informasi:",
"instance_already_running": "Sudah ada operasi YunoHost yang sedang berjalan. Tunggu itu selesai sebelum menjalankan yang lain.",
"logged_in": "Berhasil masuk",
"warning": "Peringatan:",
"cannot_open_file": "Tidak dapat membuka berkas {file} (alasan: {error})",
"error_removing": "Terjadi kesalahan ketika menghapus {path}: {error}",
"success": "Berhasil!",
"warn_the_user_about_waiting_lock": "Perintah YunoHost lain sedang berjalan saat ini, kami sedang menunggu itu selesai sebelum menjalankan yang ini",
"warn_the_user_about_waiting_lock_again": "Masih menunggu...",
"unable_authenticate": "Tidak dapat mengotentikasi",
"warn_the_user_that_lock_is_acquired": "Perintah yang tadi baru saja selesai, akan memulai perintah ini"
}

View file

@ -3,8 +3,6 @@
"password": "Password",
"argument_required": "L'argomento '{argument}' è richiesto",
"authentication_required": "Autenticazione richiesta",
"authentication_required_long": "Autenticazione richiesta per eseguire questa azione",
"colon": "{}: ",
"confirm": "Confermare {prompt}",
"deprecated_command": "'{prog} {command}' è deprecato e sarà rimosso in futuro",
"deprecated_command_alias": "'{prog} {old}' è deprecato e sarà rimosso in futuro, usa invece '{prog} {new}'",
@ -13,10 +11,7 @@
"folder_exists": "La cartella esiste già: '{path}'",
"instance_already_running": "Esiste già un'operazione YunoHost in esecuzione. Attendi il completamento prima di eseguirne un altro.",
"invalid_argument": "Argomento non valido '{argument}': {error}",
"invalid_password": "Password non valida",
"invalid_usage": "Utilizzo non valido, usa --help per vedere l'aiuto",
"ldap_attribute_already_exists": "L'attributo '{attribute}' esiste già con valore '{value}'",
"ldap_server_down": "Impossibile raggiungere il server LDAP",
"logged_in": "Connesso",
"not_logged_in": "Non hai effettuato l'accesso",
"operation_interrupted": "Operazione interrotta",
@ -25,7 +20,6 @@
"server_already_running": "Un server è già in esecuzione su quella porta",
"success": "Riuscito!",
"unable_authenticate": "Autenticazione fallita",
"unable_retrieve_session": "Impossibile recuperare la sessione perché \"{exception}\"",
"unknown_group": "Gruppo '{group}' sconosciuto",
"unknown_user": "Utente '{user}' sconosciuto",
"values_mismatch": "I valori non corrispondono",
@ -48,8 +42,5 @@
"warn_the_user_that_lock_is_acquired": "L'altro comando è appena completato, ora avvio questo comando",
"warn_the_user_about_waiting_lock_again": "Sto ancora aspettando ...",
"warn_the_user_about_waiting_lock": "Un altro comando YunoHost è in esecuzione in questo momento, stiamo aspettando che finisca prima di eseguire questo",
"corrupted_toml": "TOML corrotto da {ressource} (motivo: {error})",
"invalid_token": "Token non valido: autenticare",
"session_expired": "La sessione è terminata. Sei pregato di autenticarti nuovamente.",
"ldap_server_is_down_restart_it": "Il servizio LDAP è terminato, provo a riavviarlo..."
}
"corrupted_toml": "TOML corrotto da {ressource} (motivo: {error})"
}

1
locales/mk.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -11,11 +11,9 @@
"operation_interrupted": "Operasjon forstyrret",
"not_logged_in": "Du er ikke innlogget",
"logged_in": "Innlogget",
"invalid_password": "Ugyldig passord",
"info": "Info:",
"error": "Feil:",
"confirm": "Bekreft {prompt}",
"colon": "{}: ",
"logged_out": "Utlogget",
"password": "Passord"
}

View file

@ -4,8 +4,6 @@
"deprecated_command_alias": "'{prog} {old}' अस्वीकृत गरिएको छ र भविष्यमा हटाइनेछ, यसको सट्टा '{prog} {new}' प्रयोग गर्नुहोस्।",
"deprecated_command": "'{prog} {command}' अस्वीकृत गरिएको छ र भविष्यमा हटाइनेछ",
"confirm": "कन्फर्म {prompt}",
"colon": "{}: ",
"authentication_required_long": "यस कार्य गर्नको लागि प्रमाणीकरण आवाश्यक हुन्छ",
"authentication_required": "प्रमाणीकरण आवाश्यक छ",
"argument_required": "तर्क '{argument}' आवश्यक छ"
}

View file

@ -1,18 +1,13 @@
{
"argument_required": "Argument {argument} is vereist",
"authentication_required": "Aanmelding vereist",
"authentication_required_long": "Aanmelding is vereist om deze actie uit te voeren",
"colon": "{}: ",
"confirm": "Bevestig {prompt}",
"error": "Fout:",
"file_not_exist": "Bestand bestaat niet: '{path}'",
"folder_exists": "Deze map bestaat al: '{path}'",
"instance_already_running": "Er is al een instantie actief, bedankt om te wachten tot deze afgesloten is alvorens een andere te starten.",
"invalid_argument": "Ongeldig argument '{argument}': {error}",
"invalid_password": "Ongeldig wachtwoord",
"invalid_usage": "Ongeldig gebruik, doe --help om de hulptekst te lezen",
"ldap_attribute_already_exists": "Attribuut '{attribute}' bestaat al met waarde '{value}'",
"ldap_server_down": "Kan LDAP server niet bereiken",
"logged_in": "Ingelogd",
"logged_out": "Uitgelogd",
"not_logged_in": "U bent niet ingelogd",
@ -23,7 +18,6 @@
"server_already_running": "Er is al een server actief op die poort",
"success": "Succes!",
"unable_authenticate": "Aanmelding niet mogelijk",
"unable_retrieve_session": "Het is onmogelijk op de sessie op te halen omwille van '{exception}'",
"values_mismatch": "Waarden zijn niet gelijk",
"warning": "Waarschuwing:",
"websocket_request_expected": "Verwachtte een WebSocket request",
@ -48,6 +42,5 @@
"warn_the_user_about_waiting_lock": "Een ander YunoHost commando wordt uitgevoerd, we wachten tot het gedaan is alovrens dit te starten",
"corrupted_toml": "Ongeldige TOML werd gelezen op {ressource} (reason: {error})",
"corrupted_yaml": "Ongeldig YAML bestand op {ressource} (reason: {error})",
"invalid_token": "Ongeldig token - gelieve in te loggen",
"info": "Ter info:"
}

View file

@ -1,11 +1,9 @@
{
"argument_required": "Largument {argument} es requesit",
"authentication_required": "Autentificacion requesida",
"authentication_required_long": "Una autentificacion es requesida per acomplir aquesta accion",
"logged_in": "Connectat",
"logged_out": "Desconnectat",
"password": "Senhal",
"colon": "{}: ",
"confirm": "Confirmatz: {prompt}",
"deprecated_command": "« {prog} {command} » es despreciat e serà lèu suprimit",
"deprecated_command_alias": "« {prog} {old} » es despreciat e serà lèu suprimit, utilizatz « {prog} {new} » allòc",
@ -14,18 +12,14 @@
"folder_exists": "Lo repertòri existís ja: « {path} »",
"instance_already_running": "I a ja una operacion de YunoHost en cors. Mercés desperar que sacabe abans de ne lançar una mai.",
"invalid_argument": "Argument « {argument} » incorrècte: {error}",
"invalid_password": "Senhal incorrècte",
"ldap_server_down": "Impossible daténher lo servidor LDAP",
"not_logged_in": "Cap de session començada",
"pattern_not_match": "Correspond pas al patron",
"root_required": "Cal èsser root per realizar aquesta accion",
"unable_retrieve_session": "Recuperacion impossibla de la session a causa de « {exception} »",
"unknown_group": "Grop « {group} » desconegut",
"unknown_user": "Utilizaire « {user} » desconegut",
"values_mismatch": "Las valors correspondon pas",
"warning": "Atencion:",
"invalid_usage": "Usatge invalid, utilizatz --help per accedir a lajuda",
"ldap_attribute_already_exists": "Latribut « {attribute} » existís ja amb la valor: {value}",
"operation_interrupted": "Operacion interrompuda",
"server_already_running": "Un servidor es ja en execucion sus aqueste pòrt",
"success": "Capitada!",
@ -48,8 +42,5 @@
"corrupted_toml": "Fichièr TOML corromput en lectura de {ressource} estant (rason: {error})",
"warn_the_user_about_waiting_lock": "Una autra comanda YunoHost es en execucion, sèm a esperar quacabe abans daviar aquesta daquí",
"warn_the_user_about_waiting_lock_again": "Encara en espèra…",
"warn_the_user_that_lock_is_acquired": "lautra comanda ven dacabar, ara lançament daquesta comanda",
"invalid_token": "Geton invalid - volgatz vos autentificar",
"ldap_server_is_down_restart_it": "Lo servici LDAP ses atudat, ensajam de lo reaviar…",
"session_expired": "La session a expirat. Tornatz vos autentificar."
"warn_the_user_that_lock_is_acquired": "lautra comanda ven dacabar, ara lançament daquesta comanda"
}

View file

@ -23,7 +23,6 @@
"values_mismatch": "Wartości nie pasują",
"unknown_user": "Nieznany użytkownik '{user}'",
"unknown_group": "Nieznana grupa '{group}'",
"unable_retrieve_session": "Nie można pobrać sesji, ponieważ „{exception}”",
"unable_authenticate": "Nie można uwierzytelnić",
"success": "Sukces!",
"server_already_running": "Serwer już działa na tym porcie",
@ -32,11 +31,7 @@
"operation_interrupted": "Operacja przerwana",
"not_logged_in": "Nie jesteś zalogowany",
"logged_in": "Zalogowany",
"ldap_server_down": "Nie można połączyć się z serwerem LDAP",
"ldap_attribute_already_exists": "Atrybut „{attribute}” już istnieje z wartością „{value}”",
"invalid_usage": "Nieprawidłowe użycie. Przejdź --help, aby wyświetlić pomoc",
"invalid_token": "Nieprawidłowy token - proszę uwierzytelnić",
"invalid_password": "Nieprawidłowe hasło",
"invalid_argument": "Nieprawidłowy argument „{argument}”: {error}",
"instance_already_running": "Trwa już operacja YunoHost. Zaczekaj na zakończenie, zanim uruchomisz kolejny.",
"info": "Informacje:",
@ -46,10 +41,6 @@
"deprecated_command_alias": "„{prog} {old}” jest przestarzałe i zostanie usunięte w przyszłości, zamiast tego użyj „{prog} {new}”",
"deprecated_command": "„{prog} {command}” jest przestarzałe i zostanie usunięte w przyszłości",
"confirm": "Potwierdź {prompt}",
"colon": "{}: ",
"authentication_required_long": "Do wykonania tej czynności wymagane jest uwierzytelnienie",
"authentication_required": "Wymagane uwierzytelnienie",
"argument_required": "Argument „{argument}” jest wymagany",
"ldap_server_is_down_restart_it": "Usługa LDAP nie działa, próba restartu...",
"session_expired": "Sesja wygasła. Zaloguj się ponownie."
"argument_required": "Argument „{argument}” jest wymagany"
}

View file

@ -1,18 +1,13 @@
{
"argument_required": "O argumento '{argument}' é obrigatório",
"authentication_required": "Autenticação obrigatória",
"authentication_required_long": "É preciso autenticar-se para realizar esta ação",
"colon": "{}: ",
"confirm": "Confirmar {prompt}",
"error": "Erro:",
"file_not_exist": "O ficheiro não existe: '{path}'",
"folder_exists": "A pasta já existe: '{path}'",
"instance_already_running": "Já existe uma operação YunoHost em execução. Aguarde o término antes de executar outro.",
"invalid_argument": "Argumento inválido '{argument}': {error}",
"invalid_password": "Senha incorreta",
"invalid_usage": "Uso invalido, utilizar --help para ver a ajuda",
"ldap_attribute_already_exists": "O atributo '{attribute}' já existe com valor '{value}'",
"ldap_server_down": "Não foi possível comunicar com o servidor LDAP",
"logged_in": "Sessão iniciada",
"logged_out": "Sessão terminada",
"not_logged_in": "Não tem sessão iniciada",
@ -23,7 +18,6 @@
"server_already_running": "Existe um servidor ativo nessa porta",
"success": "Sucesso!",
"unable_authenticate": "Não foi possível autenticar",
"unable_retrieve_session": "Não foi possível recuperar a sessão porque '{exception}'",
"values_mismatch": "Os valores não coincidem",
"warning": "Aviso:",
"websocket_request_expected": "Esperado um pedido a WebSocket",
@ -48,8 +42,5 @@
"warn_the_user_about_waiting_lock_again": "Ainda esperando...",
"warn_the_user_about_waiting_lock": "Outro comando YunoHost está sendo executado agora, estamos aguardando o término antes de executar este",
"corrupted_toml": "TOML corrompido lido em {ressource} (motivo: {error})",
"invalid_token": "Token inválido - autentique",
"info": "Informações:",
"ldap_server_is_down_restart_it": "O serviço LDAP esta caído, tentando reiniciá-lo...",
"session_expired": "A sessão expirou. Se autentique de novo por favor."
"info": "Informações:"
}

View file

@ -1,8 +1,6 @@
{
"argument_required": "Требуется'{argument}' аргумент",
"authentication_required": "Требуется аутентификация",
"authentication_required_long": "Для этого действия требуется аутентификация",
"colon": "{}: ",
"confirm": "Подтвердить {prompt}",
"deprecated_command": "'{prog} {command}' устарела и будет удалена",
"deprecated_command_alias": "'{prog} {old}' устарела и будет удалена, вместо неё используйте '{prog} {new}'",
@ -10,8 +8,6 @@
"file_not_exist": "Файл не существует: '{path}'",
"folder_exists": "Каталог уже существует: '{path}'",
"invalid_argument": "Неправильный аргумент '{argument}': {error}",
"invalid_password": "Неправильный пароль",
"ldap_attribute_already_exists": "Атрибут '{attribute}' уже существует со значением '{value}'",
"logged_in": "Вы вошли",
"logged_out": "Вы вышли из системы",
"not_logged_in": "Вы не залогинились",
@ -29,7 +25,7 @@
"cannot_open_file": "Не могу открыть файл {file} (причина: {error})",
"cannot_write_file": "Не могу записать файл {file} (причина: {error})",
"unknown_error_reading_file": "Неизвестная ошибка при попытке прочитать файл {file} (причина: {error})",
"corrupted_yaml": "Повреждённой yaml получен от {ressource} (причина: {error})",
"corrupted_yaml": "Повреждённой YAML получен от {ressource} (причина: {error})",
"error_writing_file": "Ошибка при записи файла {file}: {error}",
"error_removing": "Ошибка при удалении {path}: {error}",
"invalid_url": "Неправильный url {url} (этот сайт существует ?)",
@ -38,16 +34,13 @@
"download_unknown_error": "Ошибка при загрузке данных с {url} : {error}",
"instance_already_running": "Операция YunoHost уже запущена. Пожалуйста, подождите, пока он закончится, прежде чем запускать другой.",
"root_required": "Чтобы выполнить это действие, вы должны иметь права root",
"corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})",
"corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})",
"warn_the_user_that_lock_is_acquired": "другая команда только что завершилась, теперь запускает эту команду",
"warn_the_user_about_waiting_lock_again": "Все еще жду...",
"warn_the_user_about_waiting_lock": "Сейчас запускается еще одна команда YunoHost, мы ждем ее завершения, прежде чем запустить эту",
"download_bad_status_code": "{url} вернул код состояния {code}",
"error_changing_file_permissions": "Ошибка при изменении разрешений для {path}: {error}",
"corrupted_toml": "Поврежденный том, прочитанный из {ressource} (причина: {error})",
"unable_retrieve_session": "Невозможно получить сеанс, так как '{exception}'",
"ldap_server_down": "Невозможно связаться с сервером LDAP",
"corrupted_toml": "Поврежденный TOML, прочитанный из {ressource} (причина: {error})",
"invalid_usage": "Неправильное использование, передайте --help, чтобы увидеть помощь",
"invalid_token": "Неверный токен - пожалуйста, авторизуйтесь",
"info": "Информация:"
}
}

View file

@ -17,8 +17,6 @@
"operation_interrupted": "Behandling avbruten",
"not_logged_in": "Du är inte inloggad",
"logged_in": "Inloggad",
"ldap_attribute_already_exists": "Attributet '{attribute}' finns redan med värdet '{value}'",
"invalid_password": "Ogiltigt lösenord",
"invalid_argument": "Ogiltig parameter '{argument}': {error}",
"logged_out": "Utloggad",
"info": "Info:",
@ -28,7 +26,6 @@
"deprecated_command_alias": "'{prog} {old}' rekommenderas inte längre och kommer tas bort i framtiden, använd '{prog} {new}' istället",
"deprecated_command": "'{prog} {command}' rekommenderas inte längre och kommer tas bort i framtiden",
"confirm": "Bekräfta {prompt}",
"colon": "{}: ",
"argument_required": "Parametern '{argument}' krävs",
"password": "Lösenord",
"warn_the_user_that_lock_is_acquired": "det andra kommandot har bara slutförts, nu startar du det här kommandot",
@ -42,12 +39,8 @@
"corrupted_yaml": "Skadad yaml läst från {ressource} (anledning: {error})",
"corrupted_json": "Skadad json läst från {ressource} (anledning: {error})",
"unknown_error_reading_file": "Okänt fel vid försök att läsa filen {file} (anledning: {error})",
"unable_retrieve_session": "Det gick inte att hämta sessionen eftersom '{exception}'",
"unable_authenticate": "Det går inte att verifiera",
"ldap_server_down": "Det går inte att nå LDAP-servern",
"invalid_usage": "Ogiltig användning, pass --help för att se hjälp",
"invalid_token": "Ogiltigt token - verifiera",
"instance_already_running": "Det finns redan en YunoHost-operation. Vänta tills den är klar innan du kör en annan.",
"authentication_required_long": "Autentisering krävs för att utföra denna åtgärd",
"authentication_required": "Autentisering krävs"
}

View file

@ -1,15 +1,10 @@
{
"argument_required": "{argument} argümanı gerekli",
"authentication_required": "Yetklendirme gerekli",
"authentication_required_long": "Bu işlemi yapmak içi yetkilendirme gerekli",
"colon": "{}: ",
"confirm": "{prompt}'i doğrulayın",
"error": "Hata:",
"instance_already_running": "Halihazırda bir YunoHost operasyonu var. Lütfen başka bir tane çalıştırmadan önce bitmesini bekleyin.",
"invalid_argument": "Geçersiz argüman '{argument}': {error}",
"invalid_password": "Geçersiz parola",
"ldap_attribute_already_exists": "'{attribute}={value}' özelliği zaten mevcut",
"ldap_server_down": "LDAP sunucusuna erişilemiyor",
"logged_in": "Giriş yapıldı",
"logged_out": ıkış yapıldı",
"not_logged_in": "Giriş yapmadınız",
@ -20,11 +15,10 @@
"server_already_running": "Bu portta zaten çalışan bir sunucu var",
"success": "İşlem Başarılı!",
"unable_authenticate": "Yetkilendirme başarısız",
"unable_retrieve_session": "'{exception}' nedeniyle oturum alınamadı",
"values_mismatch": "Değerler uyuşmuyor",
"warning": "Uyarı:",
"websocket_request_expected": "WebSocket isteği gerekli",
"warn_the_user_that_lock_is_acquired": "diğer komut şimdi tamamlandı, şimdi bu komutu başlatıyor",
"warn_the_user_that_lock_is_acquired": "Diğer komut şimdi tamamlandı, şimdi bu komutu başlatıyor",
"warn_the_user_about_waiting_lock_again": "Hala bekliyor...",
"warn_the_user_about_waiting_lock": "Başka bir YunoHost komutu şu anda çalışıyor, bunu çalıştırmadan önce bitmesini bekliyoruz",
"download_bad_status_code": "{url} döndürülen durum kodu {code}",
@ -35,7 +29,7 @@
"error_changing_file_permissions": "{path} için izinler değiştirilirken hata oluştu: {error}",
"error_removing": "{path} kaldırılırken hata oluştu: {error}",
"error_writing_file": "{file} dosyası yazılırken hata oluştu: {error}",
"corrupted_toml": "{ressource} kaynağından okunan bozuk toml (nedeni: {error})",
"corrupted_toml": "{ressource} kaynağından okunan bozuk TOML(nedeni: {error})",
"corrupted_yaml": "{ressource} kaynağından bozuk yaml okunuyor (nedeni: {error})",
"corrupted_json": "{ressource} adresinden okunan bozuk json (nedeni: {error})",
"unknown_error_reading_file": "{file} dosyasını okumaya çalışırken bilinmeyen hata (nedeni: {error})",
@ -44,7 +38,6 @@
"unknown_user": "Bilinmeyen '{user}' kullanıcı",
"unknown_group": "Bilinmeyen '{group}' grubu",
"invalid_usage": "Geçersiz kullanım, yardım görmek için --help iletin",
"invalid_token": "Geçersiz simge - lütfen kimlik doğrulaması yapın",
"info": "Bilgi:",
"folder_exists": "Klasör zaten var: '{path}'",
"file_not_exist": "Dosya mevcut değil: '{path}'",

View file

@ -41,7 +41,7 @@
"deprecated_command_alias": "'{prog} {old}' застаріла і буде видалена, замість неї використовуйте '{prog} {new}'",
"deprecated_command": "'{prog} {command}' застаріла і буде видалена",
"confirm": "Підтвердити {prompt}",
"colon": "{}: ",
"authentication_required": "Потрібна автентифікація",
"argument_required": "Потрібен аргумент '{argument}'"
}
"argument_required": "Потрібен аргумент '{argument}'",
"edit_text_question": "{}. Редагувати цей текст? [yN]: "
}

View file

@ -6,6 +6,7 @@ import logging
import glob
import pickle as pickle
from typing import List, Optional
from time import time
from collections import OrderedDict
from importlib import import_module
@ -30,7 +31,6 @@ logger = logging.getLogger("moulinette.actionsmap")
class _ExtraParameter(object):
"""
Argument parser for an extra parameter.
@ -39,21 +39,14 @@ class _ExtraParameter(object):
"""
name: Optional[str] = None
"""A list of interface for which the parameter doesn't apply to"""
skipped_iface: List[str] = []
def __init__(self, iface):
self.iface = iface
# Required variables
# Each extra parameters classes must overwrite these variables.
"""The extra parameter name"""
name = None
# Optional variables
# Each extra parameters classes can overwrite these variables.
"""A list of interface for which the parameter doesn't apply"""
skipped_iface = []
# Virtual methods
# Each extra parameters classes can implement these methods.
@ -621,7 +614,7 @@ class ActionsMap(object):
# This var is ['*'] by default but could be set for example to
# ['yunohost', 'yml_*']
NAMESPACE_PATTERNS = env["NAMESPACES"]
NAMESPACE_PATTERNS = env["NAMESPACES"].split()
# Look for all files that match the given patterns in the actionsmap dir
for namespace_pattern in NAMESPACE_PATTERNS:

View file

@ -22,8 +22,6 @@ for key in env.keys():
if value_from_environ:
env[key] = value_from_environ
env["NAMESPACES"] = env["NAMESPACES"].split()
def during_unittests_run():
return "TESTS_RUN" in os.environ
@ -248,7 +246,7 @@ class Moulinette18n(object):
for n in self._namespaces.values():
n.set_locale(locale)
def g(self, key, *args, **kwargs):
def g(self, key: str, *args, **kwargs) -> str:
"""Retrieve proper translation for a moulinette key
Attempt to retrieve value for a key from moulinette translations
@ -260,7 +258,7 @@ class Moulinette18n(object):
"""
return self._global.translate(key, *args, **kwargs)
def n(self, key, *args, **kwargs):
def n(self, key: str, *args, **kwargs) -> str:
"""Retrieve proper translation for a moulinette key
Attempt to retrieve value for a key from current loaded namespace
@ -273,7 +271,7 @@ class Moulinette18n(object):
"""
return self._namespaces[self._current_namespace].translate(key, *args, **kwargs)
def key_exists(self, key):
def key_exists(self, key: str) -> bool:
"""Check if a key exists in the translation files
Keyword arguments:
@ -300,7 +298,7 @@ class MoulinetteError(Exception):
super(MoulinetteError, self).__init__(msg)
self.strerror = msg
def content(self):
def content(self) -> str:
return self.strerror

View file

@ -7,6 +7,7 @@ import copy
import datetime
from collections import deque, OrderedDict
from json.encoder import JSONEncoder
from typing import Optional
from moulinette import m18n
from moulinette.core import MoulinetteError
@ -44,7 +45,7 @@ class BaseActionsMapParser(object):
# Each parser classes must implement these properties.
"""The name of the interface for which it is the parser"""
interface = None
interface: Optional[str] = None
# Virtual methods
# Each parser classes must implement these methods.

View file

@ -268,6 +268,7 @@ class Session:
return infos
@staticmethod
def delete_infos():
response.set_cookie(f"session.{Session.actionsmap_name}", "", max_age=-1)

View file

@ -2,12 +2,13 @@
import os
import sys
import getpass
import locale
import logging
import argparse
import tempfile
from collections import OrderedDict
from datetime import date, datetime
from subprocess import call
from moulinette import m18n, Moulinette
from moulinette.actionsmap import ActionsMap
@ -522,7 +523,17 @@ class Interface:
credentials = self.prompt(msg, True, False, color="yellow")
return authenticator.authenticate_credentials(credentials=credentials)
def prompt(self, message, is_password=False, confirm=False, color="blue"):
def prompt(
self,
message,
is_password=False,
confirm=False,
color="blue",
prefill="",
is_multiline=False,
autocomplete=[],
help=None,
):
"""Prompt for a value
Keyword arguments:
@ -534,15 +545,69 @@ class Interface:
"Not a tty, can't do interactive prompts", raw_msg=True
)
if is_password:
prompt = lambda m: getpass.getpass(colorize(m18n.g("colon", m), color))
else:
prompt = lambda m: input(colorize(m18n.g("colon", m), color))
value = prompt(message)
def _prompt(message):
if not is_multiline:
import prompt_toolkit
from prompt_toolkit.contrib.completers import WordCompleter
from pygments.token import Token
autocomplete_ = WordCompleter(autocomplete)
style = prompt_toolkit.styles.style_from_dict(
{
Token.Message: f"#ansi{color} bold",
}
)
def get_bottom_toolbar_tokens(cli):
if help:
return [(Token, help)]
else:
return []
def get_tokens(cli):
return [
(Token.Message, message),
(Token, ": "),
]
return prompt_toolkit.prompt(
get_prompt_tokens=get_tokens,
get_bottom_toolbar_tokens=get_bottom_toolbar_tokens,
style=style,
default=prefill,
true_color=True,
completer=autocomplete_,
is_password=is_password,
)
else:
while True:
value = input(
colorize(m18n.g("edit_text_question", message), color)
)
value = value.lower().strip()
if value in ["", "n", "no"]:
return prefill
elif value in ["y", "yes"]:
break
initial_message = prefill.encode("utf-8")
with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
tf.write(initial_message)
tf.flush()
call(["editor", tf.name])
tf.seek(0)
edited_message = tf.read()
return edited_message.decode("utf-8")
value = _prompt(message)
if confirm:
m = message[0].lower() + message[1:]
if prompt(m18n.g("confirm", prompt=m)) != value:
if _prompt(m18n.g("confirm", prompt=m)) != value:
raise MoulinetteValidationError("values_mismatch")
return value

View file

@ -15,7 +15,7 @@ from moulinette.core import MoulinetteError
# Files & directories --------------------------------------------------
def read_file(file_path):
def read_file(file_path, file_mode="r"):
"""
Read a regular text file
@ -35,7 +35,7 @@ def read_file(file_path):
# Open file and read content
try:
with open(file_path, "r") as f:
with open(file_path, file_mode) as f:
file_content = f.read()
except IOError as e:
raise MoulinetteError("cannot_open_file", file=file_path, error=str(e))
@ -67,16 +67,17 @@ def read_json(file_path):
return loaded_json
def read_yaml(file_path):
def read_yaml(file_):
"""
Safely read a yaml file
Keyword argument:
file_path -- Path to the yaml file
file -- Path or stream to the yaml file
"""
# Read file
file_content = read_file(file_path)
file_path = file_ if isinstance(file_, str) else file_.name
file_content = read_file(file_) if isinstance(file_, str) else file_
# Try to load yaml to check if it's syntaxically correct
try:
@ -118,8 +119,8 @@ def write_to_file(file_path, data, file_mode="w"):
file_mode -- Mode used when writing the file. Option meant to be used
by append_to_file to avoid duplicating the code of this function.
"""
assert isinstance(data, str) or isinstance(
data, list
assert (
isinstance(data, str) or isinstance(data, bytes) or isinstance(data, list)
), "Error: data '%s' should be either a string or a list but is of type '%s'" % (
data,
type(data),
@ -135,7 +136,7 @@ def write_to_file(file_path, data, file_mode="w"):
)
# If data is a list, check elements are strings and build a single string
if not isinstance(data, str):
if isinstance(data, list):
for element in data:
assert isinstance(
element, str
@ -364,3 +365,10 @@ def rm(path, recursive=False, force=False):
except OSError as e:
if not force:
raise MoulinetteError("error_removing", path=path, error=str(e))
def cp(source, dest, recursive=False, **kwargs):
if recursive and os.path.isdir(source):
return shutil.copytree(source, dest, symlinks=True, **kwargs)
else:
return shutil.copy2(source, dest, follow_symlinks=False, **kwargs)

View file

@ -88,6 +88,13 @@ def call_async_output(args, callback, **kwargs):
break
callback(message)
while True:
try:
callback, message = log_queue.get_nowait()
except queue.Empty:
break
callback(message)
finally:
kwargs["stdout"].close()
kwargs["stderr"].close()
@ -108,7 +115,7 @@ class LogPipe(threading.Thread):
self.log_callback = log_callback
self.fdRead, self.fdWrite = os.pipe()
self.pipeReader = os.fdopen(self.fdRead)
self.pipeReader = os.fdopen(self.fdRead, "rb")
self.queue = queue
@ -120,8 +127,8 @@ class LogPipe(threading.Thread):
def run(self):
"""Run the thread, logging everything."""
for line in iter(self.pipeReader.readline, ""):
self.queue.put((self.log_callback, line.strip("\n")))
for line in iter(self.pipeReader.readline, b""):
self.queue.put((self.log_callback, line.decode("utf-8").strip("\n")))
self.pipeReader.close()

View file

@ -2,9 +2,18 @@
import os
import sys
import subprocess
from setuptools import setup, find_packages
from moulinette import env
version = (
subprocess.check_output(
"head debian/changelog -n1 | awk '{print $2}' | tr -d '()'", shell=True
)
.decode()
.strip()
)
LOCALES_DIR = env["LOCALES_DIR"]
@ -24,6 +33,8 @@ install_deps = [
"toml",
"gevent-websocket",
"bottle",
"prompt-toolkit==1.0.15", # To be bumped to debian version once we're on bullseye (+ need tweaks in cli.py)
"pygments",
]
test_deps = [
@ -31,23 +42,24 @@ test_deps = [
"pytest-cov",
"pytest-env",
"pytest-mock",
"mock",
"requests",
"requests-mock",
"webtest",
]
extras = {
"install": install_deps,
"tests": test_deps,
}
setup(
name="Moulinette",
version="2.0.0",
version=version,
description="Prototype interfaces quickly and easily",
author="Yunohost Team",
author_email="yunohost@yunohost.org",
url="http://yunohost.org",
url="https://yunohost.org",
license="AGPL",
packages=find_packages(exclude=["test"]),
data_files=[(LOCALES_DIR, locale_files)],

View file

@ -0,0 +1,53 @@
import re
import json
import glob
# List all locale files (except en.json being the ref)
locale_folder = "locales/"
locale_files = glob.glob(locale_folder + "*.json")
locale_files = [filename.split("/")[-1] for filename in locale_files]
locale_files.remove("en.json")
reference = json.loads(open(locale_folder + "en.json").read())
def fix_locale(locale_file):
this_locale = json.loads(open(locale_folder + locale_file).read())
fixed_stuff = False
# We iterate over all keys/string in en.json
for key, string in reference.items():
# Ignore check if there's no translation yet for this key
if key not in this_locale:
continue
# Then we check that every "{stuff}" (for python's .format())
# should also be in the translated string, otherwise the .format
# will trigger an exception!
subkeys_in_ref = [k[0] for k in re.findall(r"{(\w+)(:\w)?}", string)]
subkeys_in_this_locale = [
k[0] for k in re.findall(r"{(\w+)(:\w)?}", this_locale[key])
]
if set(subkeys_in_ref) != set(subkeys_in_this_locale) and (
len(subkeys_in_ref) == len(subkeys_in_this_locale)
):
for i, subkey in enumerate(subkeys_in_ref):
this_locale[key] = this_locale[key].replace(
"{%s}" % subkeys_in_this_locale[i], "{%s}" % subkey
)
fixed_stuff = True
if fixed_stuff:
json.dump(
this_locale,
open(locale_folder + locale_file, "w"),
indent=4,
ensure_ascii=False,
)
for locale_file in locale_files:
fix_locale(locale_file)

60
test/reformat_locales.py Normal file
View file

@ -0,0 +1,60 @@
import re
def reformat(lang, transformations):
locale = open(f"locales/{lang}.json").read()
for pattern, replace in transformations.items():
locale = re.compile(pattern).sub(replace, locale)
open(f"locales/{lang}.json", "w").write(locale)
######################################################
godamn_spaces_of_hell = [
"\u00a0",
"\u2000",
"\u2001",
"\u2002",
"\u2003",
"\u2004",
"\u2005",
"\u2006",
"\u2007",
"\u2008",
"\u2009",
"\u200A",
"\u202f",
"\u202F",
"\u3000",
]
transformations = {s: " " for s in godamn_spaces_of_hell}
transformations.update(
{
"": "...",
}
)
reformat("en", transformations)
######################################################
transformations.update(
{
"courriel": "email",
"e-mail": "email",
"Courriel": "Email",
"E-mail": "Email",
"« ": "'",
"«": "'",
" »": "'",
"»": "'",
"": "'",
# r"$(\w{1,2})'|( \w{1,2})'": r"\1\2",
}
)
reformat("fr", transformations)

View file

@ -2,7 +2,7 @@ import json
import glob
from collections import OrderedDict
locale_folder = "../locales/"
locale_folder = "locales/"
locale_files = glob.glob(locale_folder + "*.json")
locale_files = [filename.split("/")[-1] for filename in locale_files]
locale_files.remove("en.json")

View file

@ -188,7 +188,7 @@ class TestAuthAPI:
class TestAuthCLI:
def test_login(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(["testauth", "default"], output_as="plain")
message = capsys.readouterr()
@ -201,25 +201,25 @@ class TestAuthCLI:
def test_login_bad_password(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="Bad Password")
mocker.patch("prompt_toolkit.prompt", return_value="Bad Password")
with pytest.raises(MoulinetteError):
moulinette_cli.run(["testauth", "default"], output_as="plain")
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="Bad Password")
mocker.patch("prompt_toolkit.prompt", return_value="Bad Password")
with pytest.raises(MoulinetteError):
moulinette_cli.run(["testauth", "default"], output_as="plain")
def test_login_wrong_profile(self, moulinette_cli, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
with pytest.raises(MoulinetteError) as exception:
moulinette_cli.run(["testauth", "other-profile"], output_as="none")
assert "invalid_password" in str(exception)
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="yoloswag")
mocker.patch("prompt_toolkit.prompt", return_value="yoloswag")
with pytest.raises(MoulinetteError) as exception:
moulinette_cli.run(["testauth", "default"], output_as="none")
@ -239,7 +239,7 @@ class TestAuthCLI:
def test_request_only_cli(self, capsys, moulinette_cli, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(["testauth", "only-cli"], output_as="plain")
message = capsys.readouterr()
@ -248,7 +248,7 @@ class TestAuthCLI:
def test_request_not_logged_only_cli(self, capsys, moulinette_cli, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass")
mocker.patch("prompt_toolkit.prompt")
with pytest.raises(MoulinetteError) as exception:
moulinette_cli.run(["testauth", "only-cli"], output_as="plain")
@ -259,7 +259,7 @@ class TestAuthCLI:
def test_request_with_callback(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(["--version"], output_as="plain")
message = capsys.readouterr()
@ -278,7 +278,7 @@ class TestAuthCLI:
def test_request_with_arg(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(["testauth", "with_arg", "yoloswag"], output_as="plain")
message = capsys.readouterr()
@ -286,7 +286,7 @@ class TestAuthCLI:
def test_request_arg_with_extra(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(
["testauth", "with_extra_str_only", "YoLoSwAg"], output_as="plain"
)
@ -306,7 +306,7 @@ class TestAuthCLI:
def test_request_arg_with_type(self, moulinette_cli, capsys, mocker):
mocker.patch("os.isatty", return_value=True)
mocker.patch("getpass.getpass", return_value="dummy")
mocker.patch("prompt_toolkit.prompt", return_value="dummy")
moulinette_cli.run(["testauth", "with_type_int", "12345"], output_as="plain")
message = capsys.readouterr()

View file

@ -1,6 +1,7 @@
import os
from subprocess import CalledProcessError
import mock
import pytest
from moulinette.utils.process import run_commands
@ -65,39 +66,71 @@ def test_run_shell_kwargs():
def test_call_async_output(test_file):
mock_callback_stdout = mock.Mock()
mock_callback_stderr = mock.Mock()
def stdout_callback(a):
assert a == "foo" or a == "bar"
mock_callback_stdout(a)
def stderr_callback(a):
assert False # we shouldn't reach this line
mock_callback_stderr(a)
callbacks = (lambda l: stdout_callback(l), lambda l: stderr_callback(l))
call_async_output(["cat", str(test_file)], callbacks)
calls = [mock.call("foo"), mock.call("bar")]
mock_callback_stdout.assert_has_calls(calls)
mock_callback_stderr.assert_not_called()
mock_callback_stdout.reset_mock()
mock_callback_stderr.reset_mock()
with pytest.raises(TypeError):
call_async_output(["cat", str(test_file)], 1)
def callbackA(a):
assert a == "foo" or a == "bar"
mock_callback_stdout.assert_not_called()
mock_callback_stderr.assert_not_called()
def callbackB(a):
assert "cat: doesntexists" in a
mock_callback_stdout.reset_mock()
mock_callback_stderr.reset_mock()
callback = (callbackA, callbackB)
def callback_stdout(a):
mock_callback_stdout(a)
def callback_stderr(a):
mock_callback_stderr(a)
callback = (callback_stdout, callback_stderr)
call_async_output(["cat", str(test_file)], callback)
call_async_output(["cat", "doesntexists"], callback)
calls = [mock.call("foo"), mock.call("bar")]
mock_callback_stdout.assert_has_calls(calls)
mock_callback_stderr.assert_not_called()
mock_callback_stdout.reset_mock()
mock_callback_stderr.reset_mock()
env_var = {"LANG": "C"}
call_async_output(["cat", "doesntexists"], callback, env=env_var)
calls = [mock.call("cat: doesntexists: No such file or directory")]
mock_callback_stdout.assert_not_called()
mock_callback_stderr.assert_has_calls(calls)
def test_call_async_output_kwargs(test_file, mocker):
mock_callback_stdout = mock.Mock()
mock_callback_stdinfo = mock.Mock()
mock_callback_stderr = mock.Mock()
def stdinfo_callback(a):
assert False # we shouldn't reach this line
mock_callback_stdinfo(a)
def stdout_callback(a):
assert a == "foo" or a == "bar"
mock_callback_stdout(a)
def stderr_callback(a):
assert False # we shouldn't reach this line
mock_callback_stderr(a)
callbacks = (
lambda l: stdout_callback(l),
@ -107,16 +140,43 @@ def test_call_async_output_kwargs(test_file, mocker):
with pytest.raises(ValueError):
call_async_output(["cat", str(test_file)], callbacks, stdout=None)
mock_callback_stdout.assert_not_called()
mock_callback_stdinfo.assert_not_called()
mock_callback_stderr.assert_not_called()
mock_callback_stdout.reset_mock()
mock_callback_stdinfo.reset_mock()
mock_callback_stderr.reset_mock()
with pytest.raises(ValueError):
call_async_output(["cat", str(test_file)], callbacks, stderr=None)
mock_callback_stdout.assert_not_called()
mock_callback_stdinfo.assert_not_called()
mock_callback_stderr.assert_not_called()
mock_callback_stdout.reset_mock()
mock_callback_stdinfo.reset_mock()
mock_callback_stderr.reset_mock()
with pytest.raises(TypeError):
call_async_output(["cat", str(test_file)], callbacks, stdinfo=None)
mock_callback_stdout.assert_not_called()
mock_callback_stdinfo.assert_not_called()
mock_callback_stderr.assert_not_called()
mock_callback_stdout.reset_mock()
mock_callback_stdinfo.reset_mock()
mock_callback_stderr.reset_mock()
dirname = os.path.dirname(str(test_file))
os.mkdir(os.path.join(dirname, "testcwd"))
call_async_output(
["cat", str(test_file)], callbacks, cwd=os.path.join(dirname, "testcwd")
)
calls = [mock.call("foo"), mock.call("bar")]
mock_callback_stdout.assert_has_calls(calls)
mock_callback_stdinfo.assert_not_called()
mock_callback_stderr.assert_not_called()
def test_check_output(test_file):

10
tox.ini
View file

@ -1,6 +1,6 @@
[tox]
envlist =
py{37,39}-{pytest,lint,invalidcode}
py{37,39}-{pytest,lint,invalidcode,mypy}
format
format-check
docs
@ -14,10 +14,12 @@ deps =
py{37,39}-pytest: .[tests]
py{37,39}-lint: flake8
py{37,39}-invalidcode: flake8
py{37,39}-mypy: mypy >= 0.761
commands =
py{37,39}-pytest: pytest {posargs} -c pytest.ini
py{37,39}-lint: flake8 moulinette test
py{37,39}-invalidcode: flake8 moulinette test --select F
py{37,39}-pytest: pytest {posargs} -c pytest.ini
py{37,39}-lint: flake8 moulinette test
py{37,39}-invalidcode: flake8 moulinette test --select F
py{37,39}-mypy: mypy --ignore-missing-imports --install-types --non-interactive moulinette/
[gh-actions]
python =