opsiom-cli 1.5.2__tar.gz → 1.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 1.5.2
3
+ ## 1.5.3
4
4
 
5
5
  ### Ajouts
6
6
  - Sous-commandes `opsiom doctor` (diagnostic), `opsiom paths` (emplacements des fichiers) et `opsiom configure`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: opsiom-cli
3
- Version: 1.5.2
3
+ Version: 1.5.3
4
4
  Summary: Interface en ligne de commande pour discuter avec Opsiom, l'assistant IA francophone.
5
5
  Keywords: chatbot,cli,francophone,ia,opsiom
6
6
  Classifier: Environment :: Console
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "opsiom-cli"
7
- version = "1.5.2"
7
+ version = "1.5.3"
8
8
  description = "Interface en ligne de commande pour discuter avec Opsiom, l'assistant IA francophone."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -12,6 +12,7 @@ import json
12
12
  import os
13
13
  import re
14
14
  import sys
15
+ import unicodedata
15
16
 
16
17
  import requests
17
18
 
@@ -91,6 +92,25 @@ def parse_login_form(page_html):
91
92
  return {"hidden": hidden, "username": user_field, "password": pass_field}
92
93
 
93
94
 
95
+ def describe_chars(text):
96
+ """Décrit chaque caractère sans le révéler : type (lettre, chiffre, espace,
97
+ symbole) ou nom Unicode pour les caractères accentués et de contrôle."""
98
+ parts = []
99
+ for i, ch in enumerate(text, start=1):
100
+ if ch.isascii() and ch.isalpha():
101
+ kind = "lettre"
102
+ elif ch.isascii() and ch.isdigit():
103
+ kind = "chiffre"
104
+ elif ch == " ":
105
+ kind = "espace"
106
+ elif ch.isascii() and ch.isprintable():
107
+ kind = "symbole"
108
+ else:
109
+ kind = unicodedata.name(ch, f"U+{ord(ch):04X}")
110
+ parts.append(f"{i}:{kind}")
111
+ return ", ".join(parts) or "(vide)"
112
+
113
+
94
114
  def _debug(message):
95
115
  if os.environ.get("OPSIOM_DEBUG"):
96
116
  print(f"[debug] {message}", file=sys.stderr)
@@ -170,6 +190,7 @@ class HFSpaceClient:
170
190
  # Les caractères de contrôle ne peuvent pas faire partie d'un mot de passe saisi
171
191
  # au clavier ; sous Windows, un Ctrl+V dans getpass les insère pourtant tels quels.
172
192
  removed = len(re.findall(r"[\x00-\x1f\x7f]", password))
193
+ _debug(f"caractères reçus du mot de passe : {describe_chars(password)}")
173
194
  password = re.sub(r"[\x00-\x1f\x7f]", "", password)
174
195
  _debug(f"pseudo envoyé : {username!r} ; mot de passe : {len(password)} caractère(s)"
175
196
  + (f", {removed} caractère(s) de contrôle supprimé(s)" if removed else ""))
@@ -176,3 +176,11 @@ def test_parse_login_form_finds_hidden_fields_and_names():
176
176
  )
177
177
  form = parse_login_form(page)
178
178
  assert form == {"hidden": {"csrf_token": "abc123"}, "username": "pseudo", "password": "mdp"}
179
+
180
+
181
+ def test_describe_chars_hides_values():
182
+ from opsiom_cli.clients import describe_chars
183
+
184
+ assert describe_chars("a1 é\x16!") == (
185
+ "1:lettre, 2:chiffre, 3:espace, 4:LATIN SMALL LETTER E WITH ACUTE, 5:U+0016, 6:symbole"
186
+ )
File without changes