Merge branch 'fix_kill_process'

- kill process automatic with interactive question to protect system
  crash
- fix erplibre installation python
- poetry update
- fix markdown translation
This commit is contained in:
Mathieu Benoit 2026-03-11 00:55:47 -04:00
commit cbe4288b87
9 changed files with 854 additions and 747 deletions

18
doc/AGENT.INFO.base.md Normal file
View file

@ -0,0 +1,18 @@
<!---------------------------->
<!-- multilingual suffix: en, fr -->
<!-- no suffix: en -->
<!---------------------------->
<!-- [en] -->
# Agent GPT information
## How to build
Source for Best practices and workflows to use with an AI agent on any project https://gist.github.com/CodeLeom/519e930f70895924f0d2b9862b95a10b
<!-- [fr] -->
# Information Agent GPT
## Comment construire
Source des meilleures pratiques et flux de travail pour utiliser un agent IA sur n'importe quel projet https://gist.github.com/CodeLeom/519e930f70895924f0d2b9862b95a10b

6
doc/AGENT.INFO.fr.md Normal file
View file

@ -0,0 +1,6 @@
# Information Agent GPT
## Comment construire
Source des meilleures pratiques et flux de travail pour utiliser un agent IA sur n'importe quel projet https://gist.github.com/CodeLeom/519e930f70895924f0d2b9862b95a10b

View file

@ -1,3 +1,4 @@
# Agent GPT information
## How to build

View file

@ -12,7 +12,6 @@ openupgradelib
unidiff
colorama
wheel
cloudflare
selenium
uvloop
python-randomword-fr
@ -32,6 +31,8 @@ cloudflare
psutil
mmg
pip-audit
tldextract
PyYAML
# For mobile
pillow
@ -44,3 +45,7 @@ git+https://github.com/psf/black.git@24.8.0
pre-commit
odoo-module-migrator
# Ignore because need installation system
# pymysql
# Don't forget to install selenium script/selenium/requirements.txt

File diff suppressed because it is too large Load diff

View file

@ -22,18 +22,18 @@ python = ">=3.12.10,<3.13"
a2wsgi = ">=1.10.6"
aiobotocore = "3.1.2"
ansi2html = "^1.9.2"
apispec = "^6.9.0"
apispec = "^6.10.0"
asn1crypto = "1.5.1"
astor = "^0.8.1"
avalara = "^26.2.0"
babel = "2.10.3"
bandit = "^1.9.3"
bandit = "^1.9.4"
base64io = "^1.0.3"
beautifulsoup4 = "4.13.5"
bokeh = "3.6.3"
boto3 = "1.42.42"
botocore = "1.42.42"
cachetools = "^7.0.1"
cachetools = "^7.0.3"
cairosvg = "^2.8.2"
caldav = "1.3.9"
cbor2 = "5.6.2"
@ -71,7 +71,7 @@ gitpython = "^3.1.46"
giturlparse = "0.12.0"
googletrans-py = "4.0.0"
greenlet = "3.0.3"
httpagentparser = "^1.9.5"
httpagentparser = "^1.9.9"
icalendar = "5.0.13"
idna = "3.6"
img2pdf = "^0.6.3"
@ -81,7 +81,7 @@ js2py = "^0.74"
libsass = "0.22.0"
lottie = "^0.7.2"
lxml = "5.2.1"
lxml-html-clean = "^0.4.3"
lxml-html-clean = "^0.4.4"
mako = "^1.3.10"
markdown2 = "2.5.1"
markdownify = "0.13.1"
@ -101,7 +101,7 @@ oauthlib = "^3.3.1"
odoorpc = "^0.10.1"
ofxparse = "0.21"
ollama = "^0.6.1"
openai = "^2.21.0"
openai = "^2.26.0"
openpyxl = "3.1.2"
openupgradelib = "^3.12.0"
openwebui-client = ">=0.3.0"
@ -114,10 +114,10 @@ pathspec = "^1.0.4"
pdf2image = "^1.17.0"
pdfminer-six = "20260107"
pexpect = "^4.9.0"
phonenumbers = "^9.0.24"
phonenumbers = "^9.0.25"
pillow = "12.1.1"
plaid-python = "^38.1.0"
plotly = "^6.5.2"
plaid-python = "^38.4.0"
plotly = "^6.6.0"
polib = "1.1.1"
pre-commit = "^4.5.1"
premailer = "^3.10.0"
@ -127,7 +127,7 @@ psycopg2 = "2.9.9"
py3dns = "^4.0.2"
py3o-formats = "^0.3"
py3o-template = "^0.10.0"
pycountry = "^24.6.1"
pycountry = "^26.2.16"
pycups = "^2.0.4"
pydantic = ">=2.0.0"
pydevd-odoo = "^1.2.2"
@ -135,8 +135,8 @@ pyftpdlib = "^2.2.0"
pygithub = "^2.8.1"
pyjsparser = "^2.7.1"
pyjwt = "^2.11.0"
pylint = "^4.0.4"
pylint-odoo = "^10.0.0"
pylint = "^4.0.5"
pylint-odoo = "^10.0.1"
pymupdf = "^1.27.1"
pyncclient = "^0.7"
pyopenssl = "25.3.0"
@ -159,7 +159,7 @@ python-resize-image = "^1.1.20"
python-slugify = "^8.0.4"
python-stdnum = "1.19"
python-telegram-bot = "^22.6"
pytz = "^2025.2"
pytz = "^2026.1.post1"
pyusb = "1.2.1"
pyyaml = "^6.0.3"
pyzbar = "^0.1.9"
@ -167,13 +167,13 @@ pyzk = "^0.9"
qifparse = "^0.5"
qrcode = "7.4.2"
radon = "^6.0.1"
redis = "^7.1.1"
regex = "^2026.1.15"
redis = "^7.3.0"
regex = "^2026.2.28"
reportlab = "4.1.0"
requests = "2.31.0"
requests = ">=2.31.0"
requests-oauthlib = "^2.0.0"
requests-toolbelt = "^1.0.0"
responses = "^0.25.8"
responses = "^0.26.0"
rjsmin = "1.2.0"
roulier = "^1.1.1"
s3fs = "2026.2.0"
@ -181,11 +181,11 @@ schwifty = "2024.4.0"
sentry-sdk = ">=2.0.0,<=2.22.0"
setuptools = "80.10.2"
shapely = "^2.1.2"
sqlalchemy = "^2.0.46"
sqlalchemy = "^2.0.48"
statsd = "^4.0.1"
tldextract = "^5.3.1"
tweepy = "^4.16.0"
twilio = "^9.10.1"
twilio = "^9.10.2"
typing-extensions = "^4.15.0"
ujson = "^5.11.0"
unidecode = "^1.4.0"
@ -217,7 +217,7 @@ rev = "replace-pyopenssl"
[tool.poetry.dependencies.webdav4]
extras = [ "fsspec",]
version = "^0.10.0"
version = "^0.11.0"
[poetry.group.dev.dependencies]
websocket_client = "^1.1.0"

View file

@ -8,6 +8,30 @@ import psutil
STOP_PARENT_KILL = ["./odoo_bin.sh", "./run.sh"]
# Processes that must never be killed — killing these
# can crash the desktop session or the system.
PROTECTED_NAMES = {
"systemd",
"init",
"gnome-session",
"gnome-session-binary",
"gnome-shell",
"gdm",
"gdm3",
"gdm-session-worker",
"lightdm",
"sddm",
"Xorg",
"Xwayland",
"plasmashell",
"kwin_wayland",
"kwin_x11",
"loginctl",
"login",
"sshd",
"tmux: server",
}
def proc_desc(p: psutil.Process) -> str:
try:
@ -131,6 +155,11 @@ def main():
default=1,
help="Kill parent too",
)
ap.add_argument(
"--automatic",
action="store_true",
help="Ignore ask confirmation before killing",
)
args = ap.parse_args()
if not (1 <= args.port <= 65535):
@ -151,20 +180,53 @@ def main():
print(f"\nListener PID {pid} ancestry:")
for i, p in enumerate(chain):
prefix = " " + ("-> " if p == target else " ")
print(prefix + proc_desc(p))
prefix = " " + ("-> " if p == target else " ")
print(f"[{i}]" + prefix + proc_desc(p))
if target is None:
continue
if target.pid == 1 and not args.allow_pid1:
if target.pid == 1:
print(
"Refus: la cible est PID 1 (systemd). Utilise plutôt systemctl pour arrêter le service.",
file=sys.stderr,
"Refus: la cible est PID 1 (systemd)."
" Utilise plutôt systemctl pour arrêter"
" le service.",
)
continue
if target.name() in PROTECTED_NAMES:
print(
f"Refus: le process '{target.name()}' semble être protégé et dangereux à être arrêter.",
)
continue
action = "KILL" if args.force else "TERM"
if not args.automatic:
has_response = False
ignore_kill = False
while not has_response and not ignore_kill:
confirm = (
input(
f"Tuer ce processus index {args.nb_parent} (enter) ou mettre "
f"l'index [0 to {len(chain) - 1}] du "
f"process à tuer, (c/C) pour annuler : \n"
)
.strip()
.lower()
)
if not confirm:
has_response = True
continue
if confirm == "c":
ignore_kill = True
print("Cancel")
continue
if confirm.isdigit():
has_response = True
target = chain[int(confirm)]
if ignore_kill:
continue
if args.kill_tree:
print(f"Target: pid={target.pid} ({action}) + children (tree)")
if not args.dry_run:
@ -173,7 +235,6 @@ def main():
print(
"Toujours vivants:",
", ".join(str(p.pid) for p in alive),
file=sys.stderr,
)
else:
print(f"Target: pid={target.pid} ({action})")

View file

@ -3,3 +3,4 @@ PyGObject==3.44.1
gst
# python-vlc
python-randomword-fr
webdriver_manager

View file

@ -897,9 +897,7 @@ class TODO:
db_name = "test_todo_tmp"
# Extra modules
extra_modules = input(
t("test_install_extra_modules")
).strip()
extra_modules = input(t("test_install_extra_modules")).strip()
# Log level
log_level = input(t("test_log_level")).strip()
@ -913,9 +911,7 @@ class TODO:
# Step 1: Create temp DB
print(f"\n--- {t('test_creating_db')} '{db_name}' ---")
cmd_restore = (
f"./script/database/db_restore.py --database {db_name}"
)
cmd_restore = f"./script/database/db_restore.py --database {db_name}"
self.execute.exec_command_live(
cmd_restore,
source_erplibre=False,
@ -923,7 +919,9 @@ class TODO:
)
# Step 2: Install modules
print(f"\n--- {t('test_installing_modules')}: {modules_to_install} ---")
print(
f"\n--- {t('test_installing_modules')}: {modules_to_install} ---"
)
cmd_install = (
f"./script/addons/install_addons.sh"
f" {db_name} {modules_to_install}"
@ -960,16 +958,12 @@ class TODO:
# Step 4: Cleanup
lang = get_lang()
keep_input = input(t("test_keep_db")).strip().lower()
keep = keep_input in (
("o", "oui") if lang == "fr" else ("y", "yes")
)
keep = keep_input in (("o", "oui") if lang == "fr" else ("y", "yes"))
if keep:
print(f"{t('test_db_kept')}: {db_name}")
else:
print(f"\n--- {t('test_cleaning_db')} '{db_name}' ---")
cmd_drop = (
f"./odoo_bin.sh db --drop --database {db_name}"
)
cmd_drop = f"./odoo_bin.sh db --drop --database {db_name}"
self.execute.exec_command_live(
cmd_drop,
source_erplibre=False,