1- # PasswordGenerator GGearing 314 01/10/19
2- # modified Prince Gangurde 4/4/2020
3-
4- import random
5-
6- import pycountry
7-
8-
9- def generate_password ():
10- # Define characters and word sets
11- special_characters = list ("!@#$%/?<>|&*-=+_" )
12-
13- animals = (
14- "ant" ,
15- "alligator" ,
16- "baboon" ,
17- "badger" ,
18- "barb" ,
19- "bat" ,
20- "beagle" ,
21- "bear" ,
22- "beaver" ,
23- "bird" ,
24- "bison" ,
25- "bombay" ,
26- "bongo" ,
27- "booby" ,
28- "butterfly" ,
29- "bee" ,
30- "camel" ,
31- "cat" ,
32- "caterpillar" ,
33- "catfish" ,
34- "cheetah" ,
35- "chicken" ,
36- "chipmunk" ,
37- "cow" ,
38- "crab" ,
39- "deer" ,
40- "dingo" ,
41- "dodo" ,
42- "dog" ,
43- "dolphin" ,
44- "donkey" ,
45- "duck" ,
46- "eagle" ,
47- "earwig" ,
48- "elephant" ,
49- "emu" ,
50- "falcon" ,
51- "ferret" ,
52- "fish" ,
53- "flamingo" ,
54- "fly" ,
55- "fox" ,
56- "frog" ,
57- "gecko" ,
58- "gibbon" ,
59- "giraffe" ,
60- "goat" ,
61- "goose" ,
62- "gorilla" ,
63- )
64-
65- colours = (
66- "red" ,
67- "orange" ,
68- "yellow" ,
69- "green" ,
70- "blue" ,
71- "indigo" ,
72- "violet" ,
73- "purple" ,
74- "magenta" ,
75- "cyan" ,
76- "pink" ,
77- "brown" ,
78- "white" ,
79- "grey" ,
80- "black" ,
81- )
82-
83- # Get random values
84- animal = random .choice (animals )
85- colour = random .choice (colours )
86- number = random .randint (1 , 999 )
87- special = random .choice (special_characters )
88- case_choice = random .choice (["upper_colour" , "upper_animal" ])
89-
90- # Pick a random country and language
91- country = random .choice (list (pycountry .countries )).name
92- languages = [lang .name for lang in pycountry .languages if hasattr (lang , "name" )]
93- language = random .choice (languages )
94-
95- # Apply casing
96- if case_choice == "upper_colour" :
1+ #!/usr/bin/env python3
2+ """
3+ Secure Password Generator – copies password to clipboard, never prints it.
4+
5+ This module generates a strong, memorable password by combining:
6+ - Two random words (animal + colour)
7+ - A random 3‑digit number
8+ - A random special character
9+
10+ It also picks a random country and language (via pycountry) to provide a
11+ memoization hint – these are NOT part of the password.
12+
13+ Security:
14+ - Uses `secrets` module for cryptographically strong randomness.
15+ - Does NOT print the password to the terminal (only a confirmation message).
16+ - Copies the password to the system clipboard using `pyperclip`.
17+
18+ Author: Modified from GGearing / Prince Gangurde
19+ Date: 2026-07-11
20+ """
21+
22+ import secrets
23+ import string
24+ from typing import Optional
25+
26+ # Optional imports with fallback
27+ try :
28+ import pyperclip # type: ignore
29+ except ImportError :
30+ pyperclip = None
31+
32+ try :
33+ import pycountry
34+ except ImportError :
35+ pycountry = None
36+
37+
38+ # ----------------------------------------------------------------------
39+ # Word lists – extend as needed, but keep them diverse
40+ # ----------------------------------------------------------------------
41+ ANIMALS = (
42+ "ant" , "bear" , "cat" , "dog" , "eagle" , "fox" , "goat" , "hawk" , "ibis" ,
43+ "jaguar" , "kangaroo" , "lion" , "monkey" , "newt" , "owl" , "panda" , "quail" ,
44+ "rabbit" , "shark" , "tiger" , "unicorn" , "vulture" , "wolf" , "xerus" ,
45+ "yak" , "zebra"
46+ )
47+
48+ COLOURS = (
49+ "red" , "orange" , "yellow" , "green" , "blue" , "indigo" , "violet" ,
50+ "purple" , "magenta" , "cyan" , "pink" , "brown" , "white" , "grey" , "black"
51+ )
52+
53+ SPECIAL_CHARS = "!@#$%/?<>|&*-=+_"
54+ DIGITS = string .digits
55+
56+
57+ # ----------------------------------------------------------------------
58+ # Core function
59+ # ----------------------------------------------------------------------
60+ def generate_secure_password (
61+ animal_list : tuple = ANIMALS ,
62+ colour_list : tuple = COLOURS ,
63+ special_chars : str = SPECIAL_CHARS ,
64+ num_digits : int = 3
65+ ) -> str :
66+ """
67+ Generate a secure, memorable password using cryptographically strong randomness.
68+
69+ The password is built as: <colour><digits><animal><special>
70+ One of the words (colour or animal) is randomly capitalised.
71+
72+ Args:
73+ animal_list: Tuple of animal names.
74+ colour_list: Tuple of colour names.
75+ special_chars: String of allowed special characters.
76+ num_digits: Number of digits to include (default 3).
77+
78+ Returns:
79+ The generated password string (not printed to console).
80+
81+ Raises:
82+ ValueError: If any word list is empty.
83+ """
84+ if not animal_list or not colour_list or not special_chars :
85+ raise ValueError ("Word lists and special chars must not be empty." )
86+
87+ # Select random elements using secrets (cryptographically secure)
88+ animal = secrets .choice (animal_list )
89+ colour = secrets .choice (colour_list )
90+
91+ # Build a random digit string of given length
92+ digit_str = '' .join (secrets .choice (DIGITS ) for _ in range (num_digits ))
93+
94+ special = secrets .choice (special_chars )
95+
96+ # Randomly choose which word to uppercase
97+ if secrets .choice ([True , False ]):
9798 colour = colour .upper ()
9899 else :
99100 animal = animal .upper ()
100101
101- # Combine to form password
102- password = f"{ colour } { number } { animal } { special } "
103- print ("Generated Password:" , password )
104- print ("Based on Country:" , country )
105- print ("Language Hint:" , language )
102+ # Assemble the password
103+ password = f"{ colour } { digit_str } { animal } { special } "
104+ return password
105+
106+
107+ def get_random_country_and_language () -> tuple [Optional [str ], Optional [str ]]:
108+ """
109+ Return a random country name and a random language name (for memorisation hints).
110+
111+ Falls back gracefully if pycountry is not installed.
112+
113+ Returns:
114+ A tuple (country_name, language_name) – either may be None.
115+ """
116+ country = None
117+ language = None
118+
119+ if pycountry is not None :
120+ try :
121+ # Pick a random country
122+ countries = list (pycountry .countries )
123+ if countries :
124+ country = secrets .choice (countries ).name
125+
126+ # Pick a random language (only those with a 'name' attribute)
127+ languages = [lang .name for lang in pycountry .languages if hasattr (lang , "name" )]
128+ if languages :
129+ language = secrets .choice (languages )
130+ except Exception :
131+ # Silently ignore any pycountry errors
132+ pass
133+
134+ return country , language
135+
136+
137+ def copy_to_clipboard (text : str ) -> bool :
138+ """
139+ Copy text to the system clipboard using pyperclip.
140+
141+ Args:
142+ text: The string to copy.
143+
144+ Returns:
145+ True if successful, False if pyperclip is not available or fails.
146+ """
147+ if pyperclip is None :
148+ return False
149+ try :
150+ pyperclip .copy (text )
151+ return True
152+ except Exception :
153+ return False
154+
155+
156+ # ----------------------------------------------------------------------
157+ # Main entry point
158+ # ----------------------------------------------------------------------
159+ def main () -> None :
160+ """
161+ Generate a password, copy it to clipboard, and show hints.
106162
163+ The password itself is never printed – only a confirmation message.
164+ """
165+ print ("🔐 Generating a secure password..." )
107166
108- # Run it
109- generate_password ()
167+ # Generate the pass
0 commit comments