Password-Generator-Advanced 1.0.0__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.
- password_generator_advanced-1.0.0/LICENSE +21 -0
- password_generator_advanced-1.0.0/PKG-INFO +11 -0
- password_generator_advanced-1.0.0/pyproject.toml +17 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/__init__.py +1 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/__main__.py +5 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/clipboard.py +26 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/generator.py +99 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/main.py +281 -0
- password_generator_advanced-1.0.0/src/password_generator_advanced/wordlist.py +2052 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 valorisa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Password-Generator-Advanced
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Générateur de mots de passe avec un menu interactif et obligation pour la création des mots de passe d'avoir des lettres minuscules, majuscules, chiffres, caractères spéciaux et un minimum de 9 chiffres et 9 caractères spéciaux.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: valorisa
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "Password-Generator-Advanced"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Générateur de mots de passe avec un menu interactif et obligation pour la création des mots de passe d'avoir des lettres minuscules, majuscules, chiffres, caractères spéciaux et un minimum de 9 chiffres et 9 caractères spéciaux."
|
|
5
|
+
authors = ["valorisa"]
|
|
6
|
+
packages = [{include = "password_generator_advanced", from = "src"}]
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = "^3.12"
|
|
10
|
+
|
|
11
|
+
[tool.poetry.group.dev.dependencies]
|
|
12
|
+
ruff = ">=0.3,<0.16"
|
|
13
|
+
pytest = "^8.0.0"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["poetry-core"]
|
|
17
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Copie dans le presse-papier (cross-platform, stdlib uniquement)."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def copy_to_clipboard(text: str) -> bool:
|
|
8
|
+
try:
|
|
9
|
+
if sys.platform == "win32":
|
|
10
|
+
process = subprocess.Popen(
|
|
11
|
+
["clip"], stdin=subprocess.PIPE, shell=True
|
|
12
|
+
)
|
|
13
|
+
process.communicate(text.encode("utf-16le"))
|
|
14
|
+
elif sys.platform == "darwin":
|
|
15
|
+
process = subprocess.Popen(
|
|
16
|
+
["pbcopy"], stdin=subprocess.PIPE
|
|
17
|
+
)
|
|
18
|
+
process.communicate(text.encode("utf-8"))
|
|
19
|
+
else:
|
|
20
|
+
process = subprocess.Popen(
|
|
21
|
+
["xclip", "-selection", "clipboard"], stdin=subprocess.PIPE
|
|
22
|
+
)
|
|
23
|
+
process.communicate(text.encode("utf-8"))
|
|
24
|
+
return process.returncode == 0
|
|
25
|
+
except (OSError, FileNotFoundError):
|
|
26
|
+
return False
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Générateur de mots de passe sécurisés avec contraintes strictes."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import secrets
|
|
5
|
+
import string
|
|
6
|
+
|
|
7
|
+
from .wordlist import WORDLIST
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DEFAULT_MIN_DIGITS = 9
|
|
11
|
+
DEFAULT_MIN_SPECIAL = 9
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def minimum_length(min_digits: int, min_special: int) -> int:
|
|
15
|
+
return min_digits + min_special + 2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_password(
|
|
19
|
+
length: int,
|
|
20
|
+
min_digits: int = DEFAULT_MIN_DIGITS,
|
|
21
|
+
min_special: int = DEFAULT_MIN_SPECIAL,
|
|
22
|
+
) -> str:
|
|
23
|
+
min_len = minimum_length(min_digits, min_special)
|
|
24
|
+
if length < min_len:
|
|
25
|
+
raise ValueError(
|
|
26
|
+
f"La longueur minimale est {min_len} "
|
|
27
|
+
f"({min_digits} chiffres + {min_special} spéciaux + 1 min + 1 maj)"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
lowercase = string.ascii_lowercase
|
|
31
|
+
uppercase = string.ascii_uppercase
|
|
32
|
+
digits = string.digits
|
|
33
|
+
special = string.punctuation
|
|
34
|
+
|
|
35
|
+
password_chars: list[str] = []
|
|
36
|
+
|
|
37
|
+
password_chars.extend(secrets.choice(digits) for _ in range(min_digits))
|
|
38
|
+
password_chars.extend(secrets.choice(special) for _ in range(min_special))
|
|
39
|
+
password_chars.append(secrets.choice(lowercase))
|
|
40
|
+
password_chars.append(secrets.choice(uppercase))
|
|
41
|
+
|
|
42
|
+
all_chars = lowercase + uppercase + digits + special
|
|
43
|
+
remaining = length - len(password_chars)
|
|
44
|
+
password_chars.extend(secrets.choice(all_chars) for _ in range(remaining))
|
|
45
|
+
|
|
46
|
+
result = list(password_chars)
|
|
47
|
+
secrets.SystemRandom().shuffle(result)
|
|
48
|
+
|
|
49
|
+
return "".join(result)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def generate_passphrase(num_words: int = 6, separator: str = "-") -> str:
|
|
53
|
+
if num_words < 1:
|
|
54
|
+
raise ValueError("Le nombre de mots doit être au moins 1")
|
|
55
|
+
words = [secrets.choice(WORDLIST) for _ in range(num_words)]
|
|
56
|
+
return separator.join(words)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def evaluate_password(password: str) -> dict:
|
|
60
|
+
length = len(password)
|
|
61
|
+
charset_size = 0
|
|
62
|
+
|
|
63
|
+
has_lower = any(c.islower() for c in password)
|
|
64
|
+
has_upper = any(c.isupper() for c in password)
|
|
65
|
+
has_digits = any(c.isdigit() for c in password)
|
|
66
|
+
has_special = any(not c.isalnum() for c in password)
|
|
67
|
+
|
|
68
|
+
if has_lower:
|
|
69
|
+
charset_size += 26
|
|
70
|
+
if has_upper:
|
|
71
|
+
charset_size += 26
|
|
72
|
+
if has_digits:
|
|
73
|
+
charset_size += 10
|
|
74
|
+
if has_special:
|
|
75
|
+
charset_size += 32
|
|
76
|
+
|
|
77
|
+
entropy = length * math.log2(charset_size) if charset_size > 0 else 0
|
|
78
|
+
|
|
79
|
+
if entropy >= 128:
|
|
80
|
+
strength = "Très fort"
|
|
81
|
+
elif entropy >= 80:
|
|
82
|
+
strength = "Fort"
|
|
83
|
+
elif entropy >= 64:
|
|
84
|
+
strength = "Moyen"
|
|
85
|
+
elif entropy >= 48:
|
|
86
|
+
strength = "Faible"
|
|
87
|
+
else:
|
|
88
|
+
strength = "Très faible"
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
"length": length,
|
|
92
|
+
"entropy": round(entropy, 1),
|
|
93
|
+
"charset_size": charset_size,
|
|
94
|
+
"has_lower": has_lower,
|
|
95
|
+
"has_upper": has_upper,
|
|
96
|
+
"has_digits": has_digits,
|
|
97
|
+
"has_special": has_special,
|
|
98
|
+
"strength": strength,
|
|
99
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Menu interactif et CLI du générateur de mots de passe."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
if sys.platform == "win32":
|
|
8
|
+
os.system("")
|
|
9
|
+
if sys.stdout.encoding.lower() != "utf-8":
|
|
10
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
11
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
12
|
+
|
|
13
|
+
from .clipboard import copy_to_clipboard
|
|
14
|
+
from .generator import (
|
|
15
|
+
DEFAULT_MIN_DIGITS,
|
|
16
|
+
DEFAULT_MIN_SPECIAL,
|
|
17
|
+
evaluate_password,
|
|
18
|
+
generate_passphrase,
|
|
19
|
+
generate_password,
|
|
20
|
+
minimum_length,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def print_header(min_digits: int, min_special: int):
|
|
25
|
+
min_len = minimum_length(min_digits, min_special)
|
|
26
|
+
print("\n" + "=" * 50)
|
|
27
|
+
print(" GÉNÉRATEUR DE MOTS DE PASSE SÉCURISÉS")
|
|
28
|
+
print("=" * 50)
|
|
29
|
+
print("\nContraintes appliquées :")
|
|
30
|
+
print(f" - Minimum {min_digits} chiffres")
|
|
31
|
+
print(f" - Minimum {min_special} caractères spéciaux")
|
|
32
|
+
print(" - Au moins 1 lettre minuscule")
|
|
33
|
+
print(" - Au moins 1 lettre majuscule")
|
|
34
|
+
print(f" - Longueur minimale : {min_len} caractères")
|
|
35
|
+
print()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_menu():
|
|
39
|
+
print("-" * 50)
|
|
40
|
+
print(" 1 - Générer un mot de passe")
|
|
41
|
+
print(" 2 - Générer plusieurs mots de passe")
|
|
42
|
+
print(" 3 - Générer une passphrase")
|
|
43
|
+
print(" 4 - Évaluer un mot de passe / passphrase")
|
|
44
|
+
print(" 5 - Quitter")
|
|
45
|
+
print("-" * 50)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_length(min_len: int) -> int:
|
|
49
|
+
while True:
|
|
50
|
+
try:
|
|
51
|
+
length = int(input(f"\nLongueur du mot de passe (min {min_len}) : "))
|
|
52
|
+
if length < min_len:
|
|
53
|
+
print(f" Erreur : minimum {min_len} caractères requis.")
|
|
54
|
+
continue
|
|
55
|
+
return length
|
|
56
|
+
except ValueError:
|
|
57
|
+
print(" Erreur : veuillez entrer un nombre entier.")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_count() -> int:
|
|
61
|
+
while True:
|
|
62
|
+
try:
|
|
63
|
+
count = int(input("\nCombien de mots de passe ? (1-50) : "))
|
|
64
|
+
if count < 1 or count > 50:
|
|
65
|
+
print(" Erreur : entre 1 et 50.")
|
|
66
|
+
continue
|
|
67
|
+
return count
|
|
68
|
+
except ValueError:
|
|
69
|
+
print(" Erreur : veuillez entrer un nombre entier.")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_num_words() -> int:
|
|
73
|
+
while True:
|
|
74
|
+
try:
|
|
75
|
+
num = int(input("\nNombre de mots (4-12, défaut 6) : ") or "6")
|
|
76
|
+
if num < 4 or num > 12:
|
|
77
|
+
print(" Erreur : entre 4 et 12 mots.")
|
|
78
|
+
continue
|
|
79
|
+
return num
|
|
80
|
+
except ValueError:
|
|
81
|
+
print(" Erreur : veuillez entrer un nombre entier.")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def offer_clipboard(text: str):
|
|
85
|
+
choice = input("\n Copier dans le presse-papier ? (o/N) : ").strip().lower()
|
|
86
|
+
if choice in ("o", "oui", "y", "yes"):
|
|
87
|
+
if copy_to_clipboard(text):
|
|
88
|
+
print(" ✓ Copié dans le presse-papier.")
|
|
89
|
+
else:
|
|
90
|
+
print(" ✗ Impossible de copier (outil clipboard non disponible).")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def display_password(password: str):
|
|
94
|
+
print("\n Mot de passe généré :")
|
|
95
|
+
print(f" {password}")
|
|
96
|
+
print(f" Longueur : {len(password)} caractères")
|
|
97
|
+
|
|
98
|
+
digits = sum(1 for c in password if c.isdigit())
|
|
99
|
+
special = sum(1 for c in password if not c.isalnum())
|
|
100
|
+
lower = sum(1 for c in password if c.islower())
|
|
101
|
+
upper = sum(1 for c in password if c.isupper())
|
|
102
|
+
|
|
103
|
+
print("\n Composition :")
|
|
104
|
+
print(f" Chiffres : {digits}")
|
|
105
|
+
print(f" Spéciaux : {special}")
|
|
106
|
+
print(f" Minuscules : {lower}")
|
|
107
|
+
print(f" Majuscules : {upper}")
|
|
108
|
+
|
|
109
|
+
offer_clipboard(password)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def display_passphrase(passphrase: str, num_words: int):
|
|
113
|
+
print("\n Passphrase générée :")
|
|
114
|
+
print(f" {passphrase}")
|
|
115
|
+
bits = num_words * 11
|
|
116
|
+
print(f" {num_words} mots — ~{bits} bits d'entropie")
|
|
117
|
+
|
|
118
|
+
offer_clipboard(passphrase)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def display_evaluation(result: dict):
|
|
122
|
+
print("\n Entropie théorique maximale :")
|
|
123
|
+
print(f" Longueur : {result['length']} caractères")
|
|
124
|
+
print(f" Entropie max. : {result['entropy']} bits")
|
|
125
|
+
print(f" Niveau : {result['strength']}")
|
|
126
|
+
print(f" Jeu de caractères: {result['charset_size']} symboles possibles")
|
|
127
|
+
print("\n Catégories détectées :")
|
|
128
|
+
print(f" Minuscules : {'✓' if result['has_lower'] else '✗'}")
|
|
129
|
+
print(f" Majuscules : {'✓' if result['has_upper'] else '✗'}")
|
|
130
|
+
print(f" Chiffres : {'✓' if result['has_digits'] else '✗'}")
|
|
131
|
+
print(f" Spéciaux : {'✓' if result['has_special'] else '✗'}")
|
|
132
|
+
print("\n ⚠ Ce calcul suppose un choix aléatoire par caractère.")
|
|
133
|
+
print(" Un mot de passe basé sur des mots du dictionnaire ou des")
|
|
134
|
+
print(" patterns prévisibles aura une entropie réelle inférieure.")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def interactive_mode(min_digits: int, min_special: int):
|
|
138
|
+
min_len = minimum_length(min_digits, min_special)
|
|
139
|
+
print_header(min_digits, min_special)
|
|
140
|
+
|
|
141
|
+
while True:
|
|
142
|
+
print_menu()
|
|
143
|
+
choice = input("\n Votre choix (1-5) : ").strip()
|
|
144
|
+
|
|
145
|
+
if choice == "1":
|
|
146
|
+
length = get_length(min_len)
|
|
147
|
+
password = generate_password(length, min_digits, min_special)
|
|
148
|
+
display_password(password)
|
|
149
|
+
|
|
150
|
+
elif choice == "2":
|
|
151
|
+
length = get_length(min_len)
|
|
152
|
+
count = get_count()
|
|
153
|
+
print(f"\n {count} mot(s) de passe de {length} caractères :\n")
|
|
154
|
+
for i in range(count):
|
|
155
|
+
password = generate_password(length, min_digits, min_special)
|
|
156
|
+
print(f" {i + 1:2d}. {password}")
|
|
157
|
+
|
|
158
|
+
elif choice == "3":
|
|
159
|
+
num_words = get_num_words()
|
|
160
|
+
passphrase = generate_passphrase(num_words)
|
|
161
|
+
display_passphrase(passphrase, num_words)
|
|
162
|
+
|
|
163
|
+
elif choice == "4":
|
|
164
|
+
pwd = input("\n Entrez le mot de passe à évaluer : ")
|
|
165
|
+
if pwd:
|
|
166
|
+
result = evaluate_password(pwd)
|
|
167
|
+
display_evaluation(result)
|
|
168
|
+
else:
|
|
169
|
+
print(" Erreur : saisie vide.")
|
|
170
|
+
|
|
171
|
+
elif choice == "5":
|
|
172
|
+
print("\n Au revoir !\n")
|
|
173
|
+
break
|
|
174
|
+
|
|
175
|
+
else:
|
|
176
|
+
print("\n Erreur : choix invalide (1-5).")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
180
|
+
parser = argparse.ArgumentParser(
|
|
181
|
+
prog="password-generator-advanced",
|
|
182
|
+
description="Générateur de mots de passe sécurisés avec contraintes strictes",
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
"--length", "-l",
|
|
186
|
+
type=int,
|
|
187
|
+
help="Longueur du mot de passe à générer",
|
|
188
|
+
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--min-digits",
|
|
191
|
+
type=int,
|
|
192
|
+
default=DEFAULT_MIN_DIGITS,
|
|
193
|
+
help=f"Nombre minimum de chiffres (défaut: {DEFAULT_MIN_DIGITS})",
|
|
194
|
+
)
|
|
195
|
+
parser.add_argument(
|
|
196
|
+
"--min-special",
|
|
197
|
+
type=int,
|
|
198
|
+
default=DEFAULT_MIN_SPECIAL,
|
|
199
|
+
help=f"Nombre minimum de caractères spéciaux (défaut: {DEFAULT_MIN_SPECIAL})",
|
|
200
|
+
)
|
|
201
|
+
parser.add_argument(
|
|
202
|
+
"--count", "-n",
|
|
203
|
+
type=int,
|
|
204
|
+
default=1,
|
|
205
|
+
help="Nombre de mots de passe à générer (défaut: 1)",
|
|
206
|
+
)
|
|
207
|
+
parser.add_argument(
|
|
208
|
+
"--passphrase", "-p",
|
|
209
|
+
action="store_true",
|
|
210
|
+
help="Générer une passphrase au lieu d'un mot de passe",
|
|
211
|
+
)
|
|
212
|
+
parser.add_argument(
|
|
213
|
+
"--words", "-w",
|
|
214
|
+
type=int,
|
|
215
|
+
default=6,
|
|
216
|
+
help="Nombre de mots pour la passphrase (défaut: 6)",
|
|
217
|
+
)
|
|
218
|
+
parser.add_argument(
|
|
219
|
+
"--separator",
|
|
220
|
+
type=str,
|
|
221
|
+
default="-",
|
|
222
|
+
help="Séparateur pour la passphrase (défaut: -)",
|
|
223
|
+
)
|
|
224
|
+
parser.add_argument(
|
|
225
|
+
"--copy", "-c",
|
|
226
|
+
action="store_true",
|
|
227
|
+
help="Copier le résultat dans le presse-papier",
|
|
228
|
+
)
|
|
229
|
+
parser.add_argument(
|
|
230
|
+
"--evaluate", "-e",
|
|
231
|
+
type=str,
|
|
232
|
+
help="Évaluer la force d'un mot de passe ou passphrase",
|
|
233
|
+
)
|
|
234
|
+
return parser
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def cli_mode(args: argparse.Namespace):
|
|
238
|
+
if args.evaluate:
|
|
239
|
+
result = evaluate_password(args.evaluate)
|
|
240
|
+
display_evaluation(result)
|
|
241
|
+
elif args.passphrase:
|
|
242
|
+
for _ in range(args.count):
|
|
243
|
+
passphrase = generate_passphrase(args.words, args.separator)
|
|
244
|
+
print(passphrase)
|
|
245
|
+
if args.copy and args.count == 1:
|
|
246
|
+
passphrase = generate_passphrase(args.words, args.separator)
|
|
247
|
+
copy_to_clipboard(passphrase)
|
|
248
|
+
else:
|
|
249
|
+
min_len = minimum_length(args.min_digits, args.min_special)
|
|
250
|
+
length = args.length if args.length else min_len
|
|
251
|
+
passwords = []
|
|
252
|
+
for _ in range(args.count):
|
|
253
|
+
password = generate_password(length, args.min_digits, args.min_special)
|
|
254
|
+
passwords.append(password)
|
|
255
|
+
print(password)
|
|
256
|
+
if args.copy and len(passwords) == 1:
|
|
257
|
+
copy_to_clipboard(passwords[0])
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def main():
|
|
261
|
+
parser = build_parser()
|
|
262
|
+
args = parser.parse_args()
|
|
263
|
+
|
|
264
|
+
has_cli_args = (
|
|
265
|
+
args.length is not None
|
|
266
|
+
or args.passphrase
|
|
267
|
+
or args.count > 1
|
|
268
|
+
or args.copy
|
|
269
|
+
or args.evaluate is not None
|
|
270
|
+
or args.min_digits != DEFAULT_MIN_DIGITS
|
|
271
|
+
or args.min_special != DEFAULT_MIN_SPECIAL
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if has_cli_args:
|
|
275
|
+
cli_mode(args)
|
|
276
|
+
else:
|
|
277
|
+
interactive_mode(args.min_digits, args.min_special)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
if __name__ == "__main__":
|
|
281
|
+
main()
|