1from random import randint, uniform, getrandbits, seed
2from typing import Tuple, Callable
3import sys
4from string import ascii_uppercase, ascii_lowercase, digits
5import datetime
6
7
8def random_int(**kwargs) -> int:
9 return randint(-sys.maxsize, sys.maxsize)
10
11
12def random_string(**kwargs) -> str:
13 STRING_CHARS = ascii_uppercase + ascii_lowercase + digits
14 return "".join(
15 STRING_CHARS[randint(0, len(STRING_CHARS) - 1)] for _ in range(randint(0, 10))
16 )
17
18
19def random_float(**kwargs) -> float:
20 return uniform(-sys.maxsize, sys.maxsize)
21
22
23def random_bool(**kwargs) -> bool:
24 return bool(getrandbits(1))
25
26
27def random_none(**kwargs) -> None:
28 return None
29
30
31def random_datetime(**kwargs) -> datetime.time:
32 return datetime.time(
33 hour=randint(0, 23),
34 minute=randint(0, 59),
35 second=randint(0, 59),
36 microsecond=randint(0, 999999),
37 )
38
39
40def random_dict(
41 max_height, max_depth, key_generators, val_generators, nested_generators, **kwargs
42):
43 non_nested_generators = tuple(set(val_generators) - set(nested_generators))
44
45 if max_depth <= 1:
46 val_generators = non_nested_generators
47
48 return generate(
49 max_height=max_height,
50 max_depth=max_depth - 1,
51 key_generators=key_generators,
52 val_generators=val_generators,
53 nested_generators=nested_generators,
54 )
55
56
57def random_array(
58 max_height, max_depth, key_generators, val_generators, nested_generators, **kwargs
59):
60 all_generators = val_generators + nested_generators
61 # Create a random list of generators
62 generators = []
63 for i in range(randint(0, max_height)):
64 # If we are at a top level depth don't allow any nested generators
65 if max_depth > 2:
66 generators.append(all_generators[randint(0, len(all_generators) - 1)])
67 else:
68 generators.append(val_generators[randint(0, len(val_generators) - 1)])
69
70 result = []
71 for generator in generators:
72 result.append(
73 generator(
74 max_height=max_height,
75 max_depth=max_depth - 1,
76 key_generators=key_generators,
77 val_generators=val_generators,
78 nested_generators=nested_generators,
79 )
80 )
81
82 return result
83
84
85def generate(
86 max_height: int = 3,
87 max_depth: int = 3,
88 rand_seed: int = None,
89 key_generators: Tuple[Callable] = (random_string,),
90 val_generators: Tuple[Callable] = (
91 random_int,
92 random_string,
93 random_float,
94 ),
95 nested_generators: Tuple[Callable] = (random_dict, random_array),
96):
97 """
98 Generate a random dictionary.
99
100 :param max_height: Maximum number of keys at a level in the dictionary
101 :param max_depth: Maximum depth of generated dictionary
102 """
103
104 # Validation Logic
105 if max_height < 1 or max_depth < 1:
106 raise AttributeError("max_height and max_depth must be greater than 0")
107
108 if rand_seed:
109 seed(rand_seed)
110
111 # Create a random list of generators
112 if max_depth > 1:
113 all_generators = val_generators + nested_generators
114 else:
115 all_generators = val_generators
116 generator_tuples = []
117 for i in range(randint(0, max_height)):
118 generator_tuples.append(
119 (
120 key_generators[randint(0, len(key_generators) - 1)],
121 all_generators[randint(0, len(all_generators) - 1)],
122 )
123 )
124
125 result = {}
126 for key_gen, val_gen in generator_tuples:
127 if max_depth > 1:
128 new_val = val_gen(
129 max_height=max_height,
130 max_depth=max_depth,
131 key_generators=key_generators,
132 val_generators=val_generators,
133 nested_generators=nested_generators,
134 )
135 else:
136 new_val = val_gen()
137 result[key_gen()] = new_val
138
139 return result