dataforge-py 0.2.0__py3-none-any.whl
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.
- dataforge/__init__.py +20 -0
- dataforge/backend.py +147 -0
- dataforge/cli.py +166 -0
- dataforge/core.py +1169 -0
- dataforge/locales/__init__.py +1 -0
- dataforge/locales/ar_SA/__init__.py +1 -0
- dataforge/locales/ar_SA/address.py +128 -0
- dataforge/locales/ar_SA/company.py +183 -0
- dataforge/locales/ar_SA/internet.py +25 -0
- dataforge/locales/ar_SA/person.py +217 -0
- dataforge/locales/ar_SA/phone.py +15 -0
- dataforge/locales/de_DE/__init__.py +1 -0
- dataforge/locales/de_DE/address.py +148 -0
- dataforge/locales/de_DE/company.py +125 -0
- dataforge/locales/de_DE/internet.py +32 -0
- dataforge/locales/de_DE/person.py +212 -0
- dataforge/locales/de_DE/phone.py +17 -0
- dataforge/locales/en_AU/__init__.py +1 -0
- dataforge/locales/en_AU/address.py +231 -0
- dataforge/locales/en_AU/company.py +193 -0
- dataforge/locales/en_AU/internet.py +34 -0
- dataforge/locales/en_AU/person.py +370 -0
- dataforge/locales/en_AU/phone.py +16 -0
- dataforge/locales/en_CA/__init__.py +1 -0
- dataforge/locales/en_CA/address.py +276 -0
- dataforge/locales/en_CA/company.py +193 -0
- dataforge/locales/en_CA/internet.py +34 -0
- dataforge/locales/en_CA/person.py +377 -0
- dataforge/locales/en_CA/phone.py +15 -0
- dataforge/locales/en_GB/__init__.py +1 -0
- dataforge/locales/en_GB/address.py +312 -0
- dataforge/locales/en_GB/company.py +196 -0
- dataforge/locales/en_GB/internet.py +34 -0
- dataforge/locales/en_GB/person.py +372 -0
- dataforge/locales/en_GB/phone.py +15 -0
- dataforge/locales/en_US/__init__.py +1 -0
- dataforge/locales/en_US/address.py +268 -0
- dataforge/locales/en_US/company.py +191 -0
- dataforge/locales/en_US/internet.py +34 -0
- dataforge/locales/en_US/person.py +370 -0
- dataforge/locales/en_US/phone.py +15 -0
- dataforge/locales/es_ES/__init__.py +1 -0
- dataforge/locales/es_ES/address.py +151 -0
- dataforge/locales/es_ES/company.py +125 -0
- dataforge/locales/es_ES/internet.py +30 -0
- dataforge/locales/es_ES/person.py +207 -0
- dataforge/locales/es_ES/phone.py +15 -0
- dataforge/locales/fr_FR/__init__.py +1 -0
- dataforge/locales/fr_FR/address.py +145 -0
- dataforge/locales/fr_FR/company.py +125 -0
- dataforge/locales/fr_FR/internet.py +30 -0
- dataforge/locales/fr_FR/person.py +212 -0
- dataforge/locales/fr_FR/phone.py +15 -0
- dataforge/locales/hi_IN/__init__.py +1 -0
- dataforge/locales/hi_IN/address.py +177 -0
- dataforge/locales/hi_IN/company.py +191 -0
- dataforge/locales/hi_IN/internet.py +26 -0
- dataforge/locales/hi_IN/person.py +218 -0
- dataforge/locales/hi_IN/phone.py +21 -0
- dataforge/locales/it_IT/__init__.py +1 -0
- dataforge/locales/it_IT/address.py +218 -0
- dataforge/locales/it_IT/company.py +151 -0
- dataforge/locales/it_IT/internet.py +31 -0
- dataforge/locales/it_IT/person.py +187 -0
- dataforge/locales/it_IT/phone.py +15 -0
- dataforge/locales/ja_JP/__init__.py +1 -0
- dataforge/locales/ja_JP/address.py +174 -0
- dataforge/locales/ja_JP/company.py +121 -0
- dataforge/locales/ja_JP/internet.py +30 -0
- dataforge/locales/ja_JP/person.py +207 -0
- dataforge/locales/ja_JP/phone.py +18 -0
- dataforge/locales/ko_KR/__init__.py +1 -0
- dataforge/locales/ko_KR/address.py +121 -0
- dataforge/locales/ko_KR/company.py +151 -0
- dataforge/locales/ko_KR/internet.py +30 -0
- dataforge/locales/ko_KR/person.py +157 -0
- dataforge/locales/ko_KR/phone.py +26 -0
- dataforge/locales/nl_NL/__init__.py +1 -0
- dataforge/locales/nl_NL/address.py +152 -0
- dataforge/locales/nl_NL/company.py +182 -0
- dataforge/locales/nl_NL/internet.py +41 -0
- dataforge/locales/nl_NL/person.py +218 -0
- dataforge/locales/nl_NL/phone.py +19 -0
- dataforge/locales/pl_PL/__init__.py +1 -0
- dataforge/locales/pl_PL/address.py +140 -0
- dataforge/locales/pl_PL/company.py +183 -0
- dataforge/locales/pl_PL/internet.py +36 -0
- dataforge/locales/pl_PL/person.py +217 -0
- dataforge/locales/pl_PL/phone.py +15 -0
- dataforge/locales/pt_BR/__init__.py +1 -0
- dataforge/locales/pt_BR/address.py +127 -0
- dataforge/locales/pt_BR/company.py +151 -0
- dataforge/locales/pt_BR/internet.py +31 -0
- dataforge/locales/pt_BR/person.py +187 -0
- dataforge/locales/pt_BR/phone.py +15 -0
- dataforge/locales/ru_RU/__init__.py +1 -0
- dataforge/locales/ru_RU/address.py +156 -0
- dataforge/locales/ru_RU/company.py +168 -0
- dataforge/locales/ru_RU/internet.py +26 -0
- dataforge/locales/ru_RU/person.py +218 -0
- dataforge/locales/ru_RU/phone.py +16 -0
- dataforge/locales/zh_CN/__init__.py +1 -0
- dataforge/locales/zh_CN/address.py +141 -0
- dataforge/locales/zh_CN/company.py +151 -0
- dataforge/locales/zh_CN/internet.py +30 -0
- dataforge/locales/zh_CN/person.py +157 -0
- dataforge/locales/zh_CN/phone.py +25 -0
- dataforge/providers/__init__.py +1 -0
- dataforge/providers/address.py +460 -0
- dataforge/providers/ai_chat.py +170 -0
- dataforge/providers/ai_prompt.py +447 -0
- dataforge/providers/automotive.py +416 -0
- dataforge/providers/barcode.py +149 -0
- dataforge/providers/base.py +34 -0
- dataforge/providers/color.py +247 -0
- dataforge/providers/company.py +144 -0
- dataforge/providers/crypto.py +105 -0
- dataforge/providers/datetime.py +397 -0
- dataforge/providers/ecommerce.py +316 -0
- dataforge/providers/education.py +234 -0
- dataforge/providers/file.py +271 -0
- dataforge/providers/finance.py +545 -0
- dataforge/providers/geo.py +332 -0
- dataforge/providers/government.py +114 -0
- dataforge/providers/internet.py +351 -0
- dataforge/providers/llm.py +726 -0
- dataforge/providers/lorem.py +241 -0
- dataforge/providers/medical.py +364 -0
- dataforge/providers/misc.py +196 -0
- dataforge/providers/network.py +283 -0
- dataforge/providers/payment.py +300 -0
- dataforge/providers/person.py +195 -0
- dataforge/providers/phone.py +87 -0
- dataforge/providers/profile.py +265 -0
- dataforge/providers/science.py +365 -0
- dataforge/providers/text.py +365 -0
- dataforge/py.typed +0 -0
- dataforge/pytest_plugin.py +80 -0
- dataforge/registry.py +164 -0
- dataforge/schema.py +772 -0
- dataforge/unique.py +171 -0
- dataforge_py-0.2.0.dist-info/METADATA +964 -0
- dataforge_py-0.2.0.dist-info/RECORD +145 -0
- dataforge_py-0.2.0.dist-info/WHEEL +4 -0
- dataforge_py-0.2.0.dist-info/entry_points.txt +35 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""AutomotiveProvider — generates fake vehicle-related data.
|
|
2
|
+
|
|
3
|
+
Includes license plates, VINs, vehicle makes, models, years, and colors.
|
|
4
|
+
All data is stored as immutable ``tuple[str, ...]`` for cache friendliness.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Literal, overload
|
|
8
|
+
|
|
9
|
+
from dataforge.providers.base import BaseProvider
|
|
10
|
+
|
|
11
|
+
# ------------------------------------------------------------------
|
|
12
|
+
# Data tuples (immutable, module-level for zero per-call overhead)
|
|
13
|
+
# ------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
_VEHICLE_MAKES: tuple[str, ...] = (
|
|
16
|
+
"Toyota",
|
|
17
|
+
"Honda",
|
|
18
|
+
"Ford",
|
|
19
|
+
"Chevrolet",
|
|
20
|
+
"BMW",
|
|
21
|
+
"Mercedes-Benz",
|
|
22
|
+
"Audi",
|
|
23
|
+
"Volkswagen",
|
|
24
|
+
"Hyundai",
|
|
25
|
+
"Kia",
|
|
26
|
+
"Nissan",
|
|
27
|
+
"Subaru",
|
|
28
|
+
"Mazda",
|
|
29
|
+
"Lexus",
|
|
30
|
+
"Tesla",
|
|
31
|
+
"Jeep",
|
|
32
|
+
"Ram",
|
|
33
|
+
"GMC",
|
|
34
|
+
"Dodge",
|
|
35
|
+
"Buick",
|
|
36
|
+
"Cadillac",
|
|
37
|
+
"Lincoln",
|
|
38
|
+
"Acura",
|
|
39
|
+
"Infiniti",
|
|
40
|
+
"Volvo",
|
|
41
|
+
"Porsche",
|
|
42
|
+
"Land Rover",
|
|
43
|
+
"Jaguar",
|
|
44
|
+
"Mitsubishi",
|
|
45
|
+
"Chrysler",
|
|
46
|
+
"Fiat",
|
|
47
|
+
"Alfa Romeo",
|
|
48
|
+
"Genesis",
|
|
49
|
+
"Rivian",
|
|
50
|
+
"Lucid",
|
|
51
|
+
"Polestar",
|
|
52
|
+
"Mini",
|
|
53
|
+
"Maserati",
|
|
54
|
+
"Ferrari",
|
|
55
|
+
"Lamborghini",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
_VEHICLE_MODELS: tuple[str, ...] = (
|
|
59
|
+
"Camry",
|
|
60
|
+
"Civic",
|
|
61
|
+
"F-150",
|
|
62
|
+
"Silverado",
|
|
63
|
+
"3 Series",
|
|
64
|
+
"C-Class",
|
|
65
|
+
"A4",
|
|
66
|
+
"Golf",
|
|
67
|
+
"Elantra",
|
|
68
|
+
"Forte",
|
|
69
|
+
"Altima",
|
|
70
|
+
"Outback",
|
|
71
|
+
"CX-5",
|
|
72
|
+
"RX",
|
|
73
|
+
"Model 3",
|
|
74
|
+
"Wrangler",
|
|
75
|
+
"1500",
|
|
76
|
+
"Sierra",
|
|
77
|
+
"Charger",
|
|
78
|
+
"Encore",
|
|
79
|
+
"Escalade",
|
|
80
|
+
"Navigator",
|
|
81
|
+
"MDX",
|
|
82
|
+
"Q50",
|
|
83
|
+
"XC90",
|
|
84
|
+
"911",
|
|
85
|
+
"Range Rover",
|
|
86
|
+
"F-Type",
|
|
87
|
+
"Outlander",
|
|
88
|
+
"Pacifica",
|
|
89
|
+
"500",
|
|
90
|
+
"Giulia",
|
|
91
|
+
"G70",
|
|
92
|
+
"R1T",
|
|
93
|
+
"Air",
|
|
94
|
+
"Polestar 2",
|
|
95
|
+
"Cooper",
|
|
96
|
+
"Ghibli",
|
|
97
|
+
"488",
|
|
98
|
+
"Urus",
|
|
99
|
+
"Corolla",
|
|
100
|
+
"Accord",
|
|
101
|
+
"Mustang",
|
|
102
|
+
"Malibu",
|
|
103
|
+
"X5",
|
|
104
|
+
"E-Class",
|
|
105
|
+
"Q7",
|
|
106
|
+
"Passat",
|
|
107
|
+
"Tucson",
|
|
108
|
+
"Sportage",
|
|
109
|
+
"Sentra",
|
|
110
|
+
"Forester",
|
|
111
|
+
"Mazda3",
|
|
112
|
+
"ES",
|
|
113
|
+
"Model Y",
|
|
114
|
+
"Grand Cherokee",
|
|
115
|
+
"2500",
|
|
116
|
+
"Yukon",
|
|
117
|
+
"Challenger",
|
|
118
|
+
"Enclave",
|
|
119
|
+
"CT5",
|
|
120
|
+
"Corsair",
|
|
121
|
+
"TLX",
|
|
122
|
+
"QX60",
|
|
123
|
+
"XC60",
|
|
124
|
+
"Cayenne",
|
|
125
|
+
"Defender",
|
|
126
|
+
"XE",
|
|
127
|
+
"Eclipse Cross",
|
|
128
|
+
"300",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
_VEHICLE_COLORS: tuple[str, ...] = (
|
|
132
|
+
"Black",
|
|
133
|
+
"White",
|
|
134
|
+
"Silver",
|
|
135
|
+
"Gray",
|
|
136
|
+
"Red",
|
|
137
|
+
"Blue",
|
|
138
|
+
"Green",
|
|
139
|
+
"Brown",
|
|
140
|
+
"Beige",
|
|
141
|
+
"Gold",
|
|
142
|
+
"Orange",
|
|
143
|
+
"Yellow",
|
|
144
|
+
"Purple",
|
|
145
|
+
"Burgundy",
|
|
146
|
+
"Navy",
|
|
147
|
+
"Charcoal",
|
|
148
|
+
"Pearl White",
|
|
149
|
+
"Midnight Blue",
|
|
150
|
+
"Racing Red",
|
|
151
|
+
"Arctic Silver",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
_PLATE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
155
|
+
_PLATE_DIGITS = "0123456789"
|
|
156
|
+
|
|
157
|
+
# VIN character set (excludes I, O, Q per standard)
|
|
158
|
+
_VIN_CHARS = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789"
|
|
159
|
+
_VIN_WEIGHTS = (8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2)
|
|
160
|
+
_VIN_TRANSLITERATE = {
|
|
161
|
+
"A": 1,
|
|
162
|
+
"B": 2,
|
|
163
|
+
"C": 3,
|
|
164
|
+
"D": 4,
|
|
165
|
+
"E": 5,
|
|
166
|
+
"F": 6,
|
|
167
|
+
"G": 7,
|
|
168
|
+
"H": 8,
|
|
169
|
+
"J": 1,
|
|
170
|
+
"K": 2,
|
|
171
|
+
"L": 3,
|
|
172
|
+
"M": 4,
|
|
173
|
+
"N": 5,
|
|
174
|
+
"P": 7,
|
|
175
|
+
"R": 9,
|
|
176
|
+
"S": 2,
|
|
177
|
+
"T": 3,
|
|
178
|
+
"U": 4,
|
|
179
|
+
"V": 5,
|
|
180
|
+
"W": 6,
|
|
181
|
+
"X": 7,
|
|
182
|
+
"Y": 8,
|
|
183
|
+
"Z": 9,
|
|
184
|
+
"0": 0,
|
|
185
|
+
"1": 1,
|
|
186
|
+
"2": 2,
|
|
187
|
+
"3": 3,
|
|
188
|
+
"4": 4,
|
|
189
|
+
"5": 5,
|
|
190
|
+
"6": 6,
|
|
191
|
+
"7": 7,
|
|
192
|
+
"8": 8,
|
|
193
|
+
"9": 9,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class AutomotiveProvider(BaseProvider):
|
|
198
|
+
"""Generates fake automotive / vehicle data.
|
|
199
|
+
|
|
200
|
+
This provider is locale-independent.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
engine : RandomEngine
|
|
205
|
+
The shared random engine instance.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
__slots__ = ()
|
|
209
|
+
|
|
210
|
+
_provider_name = "automotive"
|
|
211
|
+
_locale_modules: tuple[str, ...] = ()
|
|
212
|
+
_field_map: dict[str, str] = {
|
|
213
|
+
"license_plate": "license_plate",
|
|
214
|
+
"vin": "vin",
|
|
215
|
+
"vehicle_make": "vehicle_make",
|
|
216
|
+
"vehicle_model": "vehicle_model",
|
|
217
|
+
"vehicle_year": "vehicle_year_str",
|
|
218
|
+
"vehicle_color": "vehicle_color",
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# ------------------------------------------------------------------
|
|
222
|
+
# Scalar helpers
|
|
223
|
+
# ------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
def _one_plate(self) -> str:
|
|
226
|
+
"""Generate a single US-style license plate (ABC-1234)."""
|
|
227
|
+
choice = self._engine._rng.choice
|
|
228
|
+
a = choice(_PLATE_LETTERS)
|
|
229
|
+
b = choice(_PLATE_LETTERS)
|
|
230
|
+
c = choice(_PLATE_LETTERS)
|
|
231
|
+
d = choice(_PLATE_DIGITS)
|
|
232
|
+
e = choice(_PLATE_DIGITS)
|
|
233
|
+
f = choice(_PLATE_DIGITS)
|
|
234
|
+
g = choice(_PLATE_DIGITS)
|
|
235
|
+
return f"{a}{b}{c}-{d}{e}{f}{g}"
|
|
236
|
+
|
|
237
|
+
def _one_vin(self) -> str:
|
|
238
|
+
"""Generate a single 17-char VIN with valid check digit."""
|
|
239
|
+
choices = self._engine._rng.choices
|
|
240
|
+
# Positions: 0-7 (WMI + VDS), 8 (check digit), 9-16 (VIS)
|
|
241
|
+
chars = choices(_VIN_CHARS, k=17)
|
|
242
|
+
# Calculate check digit (position 8)
|
|
243
|
+
total = 0
|
|
244
|
+
for i, ch in enumerate(chars):
|
|
245
|
+
if i == 8:
|
|
246
|
+
continue # skip check digit position
|
|
247
|
+
total += _VIN_TRANSLITERATE[ch] * _VIN_WEIGHTS[i]
|
|
248
|
+
remainder = total % 11
|
|
249
|
+
chars[8] = "X" if remainder == 10 else str(remainder)
|
|
250
|
+
return "".join(chars)
|
|
251
|
+
|
|
252
|
+
# ------------------------------------------------------------------
|
|
253
|
+
# Public API
|
|
254
|
+
# ------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
@overload
|
|
257
|
+
def license_plate(self) -> str: ...
|
|
258
|
+
@overload
|
|
259
|
+
def license_plate(self, count: Literal[1]) -> str: ...
|
|
260
|
+
@overload
|
|
261
|
+
def license_plate(self, count: int) -> str | list[str]: ...
|
|
262
|
+
def license_plate(self, count: int = 1) -> str | list[str]:
|
|
263
|
+
"""Generate a US-style license plate (e.g. ``"ABC-1234"``).
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
count : int
|
|
268
|
+
Number of plates to generate.
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
str or list[str]
|
|
273
|
+
"""
|
|
274
|
+
if count == 1:
|
|
275
|
+
return self._one_plate()
|
|
276
|
+
return [self._one_plate() for _ in range(count)]
|
|
277
|
+
|
|
278
|
+
@overload
|
|
279
|
+
def vin(self) -> str: ...
|
|
280
|
+
@overload
|
|
281
|
+
def vin(self, count: Literal[1]) -> str: ...
|
|
282
|
+
@overload
|
|
283
|
+
def vin(self, count: int) -> str | list[str]: ...
|
|
284
|
+
def vin(self, count: int = 1) -> str | list[str]:
|
|
285
|
+
"""Generate a 17-character Vehicle Identification Number.
|
|
286
|
+
|
|
287
|
+
The check digit (position 9) is computed correctly per the
|
|
288
|
+
ISO 3779 / FMVSS 115 algorithm.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
count : int
|
|
293
|
+
Number of VINs to generate.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
str or list[str]
|
|
298
|
+
"""
|
|
299
|
+
if count == 1:
|
|
300
|
+
return self._one_vin()
|
|
301
|
+
return [self._one_vin() for _ in range(count)]
|
|
302
|
+
|
|
303
|
+
@overload
|
|
304
|
+
def vehicle_make(self) -> str: ...
|
|
305
|
+
@overload
|
|
306
|
+
def vehicle_make(self, count: Literal[1]) -> str: ...
|
|
307
|
+
@overload
|
|
308
|
+
def vehicle_make(self, count: int) -> str | list[str]: ...
|
|
309
|
+
def vehicle_make(self, count: int = 1) -> str | list[str]:
|
|
310
|
+
"""Generate a vehicle manufacturer name (e.g. ``"Toyota"``).
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
count : int
|
|
315
|
+
Number of makes to generate.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
str or list[str]
|
|
320
|
+
"""
|
|
321
|
+
if count == 1:
|
|
322
|
+
return self._engine.choice(_VEHICLE_MAKES)
|
|
323
|
+
return self._engine.choices(_VEHICLE_MAKES, count)
|
|
324
|
+
|
|
325
|
+
@overload
|
|
326
|
+
def vehicle_model(self) -> str: ...
|
|
327
|
+
@overload
|
|
328
|
+
def vehicle_model(self, count: Literal[1]) -> str: ...
|
|
329
|
+
@overload
|
|
330
|
+
def vehicle_model(self, count: int) -> str | list[str]: ...
|
|
331
|
+
def vehicle_model(self, count: int = 1) -> str | list[str]:
|
|
332
|
+
"""Generate a vehicle model name (e.g. ``"Camry"``).
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
count : int
|
|
337
|
+
Number of models to generate.
|
|
338
|
+
|
|
339
|
+
Returns
|
|
340
|
+
-------
|
|
341
|
+
str or list[str]
|
|
342
|
+
"""
|
|
343
|
+
if count == 1:
|
|
344
|
+
return self._engine.choice(_VEHICLE_MODELS)
|
|
345
|
+
return self._engine.choices(_VEHICLE_MODELS, count)
|
|
346
|
+
|
|
347
|
+
@overload
|
|
348
|
+
def vehicle_year(self) -> int: ...
|
|
349
|
+
@overload
|
|
350
|
+
def vehicle_year(self, count: Literal[1]) -> int: ...
|
|
351
|
+
@overload
|
|
352
|
+
def vehicle_year(self, count: int) -> int | list[int]: ...
|
|
353
|
+
def vehicle_year(self, count: int = 1) -> int | list[int]:
|
|
354
|
+
"""Generate a vehicle model year (1990–2026).
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
count : int
|
|
359
|
+
Number of years to generate.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
int or list[int]
|
|
364
|
+
"""
|
|
365
|
+
ri = self._engine.random_int
|
|
366
|
+
if count == 1:
|
|
367
|
+
return ri(1990, 2026)
|
|
368
|
+
return [ri(1990, 2026) for _ in range(count)]
|
|
369
|
+
|
|
370
|
+
@overload
|
|
371
|
+
def vehicle_year_str(self) -> str: ...
|
|
372
|
+
@overload
|
|
373
|
+
def vehicle_year_str(self, count: Literal[1]) -> str: ...
|
|
374
|
+
@overload
|
|
375
|
+
def vehicle_year_str(self, count: int) -> str | list[str]: ...
|
|
376
|
+
def vehicle_year_str(self, count: int = 1) -> str | list[str]:
|
|
377
|
+
"""Generate a vehicle model year as a string (``"1990"``–``"2026"``).
|
|
378
|
+
|
|
379
|
+
This variant is used by the ``_field_map`` for Schema
|
|
380
|
+
compatibility (all Schema fields must produce strings).
|
|
381
|
+
|
|
382
|
+
Parameters
|
|
383
|
+
----------
|
|
384
|
+
count : int
|
|
385
|
+
Number of years to generate.
|
|
386
|
+
|
|
387
|
+
Returns
|
|
388
|
+
-------
|
|
389
|
+
str or list[str]
|
|
390
|
+
"""
|
|
391
|
+
ri = self._engine.random_int
|
|
392
|
+
if count == 1:
|
|
393
|
+
return str(ri(1990, 2026))
|
|
394
|
+
return [str(ri(1990, 2026)) for _ in range(count)]
|
|
395
|
+
|
|
396
|
+
@overload
|
|
397
|
+
def vehicle_color(self) -> str: ...
|
|
398
|
+
@overload
|
|
399
|
+
def vehicle_color(self, count: Literal[1]) -> str: ...
|
|
400
|
+
@overload
|
|
401
|
+
def vehicle_color(self, count: int) -> str | list[str]: ...
|
|
402
|
+
def vehicle_color(self, count: int = 1) -> str | list[str]:
|
|
403
|
+
"""Generate a vehicle color (e.g. ``"Midnight Blue"``).
|
|
404
|
+
|
|
405
|
+
Parameters
|
|
406
|
+
----------
|
|
407
|
+
count : int
|
|
408
|
+
Number of colors to generate.
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
str or list[str]
|
|
413
|
+
"""
|
|
414
|
+
if count == 1:
|
|
415
|
+
return self._engine.choice(_VEHICLE_COLORS)
|
|
416
|
+
return self._engine.choices(_VEHICLE_COLORS, count)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Barcode provider — generates fake EAN, ISBN, and UPC barcodes.
|
|
2
|
+
|
|
3
|
+
All barcodes include valid check digits computed per their respective
|
|
4
|
+
standards. This provider is locale-independent.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Literal, overload
|
|
8
|
+
|
|
9
|
+
from dataforge.providers.base import BaseProvider
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _ean_check_digit(digits: str) -> str:
|
|
13
|
+
"""Compute EAN/ISBN-13 check digit (mod-10, weight 1/3 alternating).
|
|
14
|
+
|
|
15
|
+
Uses alternating-slice sums on ``ord``-based integer tuples —
|
|
16
|
+
eliminates the ``enumerate`` loop and per-iteration ``i % 2``
|
|
17
|
+
branching entirely.
|
|
18
|
+
"""
|
|
19
|
+
d = tuple(ord(ch) - 48 for ch in digits)
|
|
20
|
+
total = sum(d[::2]) + 3 * sum(d[1::2])
|
|
21
|
+
return str((10 - (total % 10)) % 10)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _isbn10_check_digit(digits: str) -> str:
|
|
25
|
+
"""Compute ISBN-10 check digit (mod-11, weights 10..2)."""
|
|
26
|
+
total = sum((ord(d) - 48) * (10 - i) for i, d in enumerate(digits))
|
|
27
|
+
remainder = (11 - (total % 11)) % 11
|
|
28
|
+
return "X" if remainder == 10 else str(remainder)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BarcodeProvider(BaseProvider):
|
|
32
|
+
"""Generates fake barcodes with valid check digits.
|
|
33
|
+
|
|
34
|
+
This provider is locale-independent.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
engine : RandomEngine
|
|
39
|
+
The shared random engine instance.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
__slots__ = ()
|
|
43
|
+
|
|
44
|
+
_provider_name = "barcode"
|
|
45
|
+
_locale_modules = ()
|
|
46
|
+
_field_map = {
|
|
47
|
+
"ean13": "ean13",
|
|
48
|
+
"ean8": "ean8",
|
|
49
|
+
"isbn13": "isbn13",
|
|
50
|
+
"isbn10": "isbn10",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ------------------------------------------------------------------
|
|
54
|
+
# Scalar helpers
|
|
55
|
+
# ------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
def _one_ean13(self) -> str:
|
|
58
|
+
body = self._engine.random_digits_str(12)
|
|
59
|
+
return body + _ean_check_digit(body)
|
|
60
|
+
|
|
61
|
+
def _one_ean8(self) -> str:
|
|
62
|
+
body = self._engine.random_digits_str(7)
|
|
63
|
+
return body + _ean_check_digit(body)
|
|
64
|
+
|
|
65
|
+
def _one_isbn13(self) -> str:
|
|
66
|
+
# ISBN-13 starts with 978 or 979
|
|
67
|
+
prefix = self._engine.choice(("978", "979"))
|
|
68
|
+
body = prefix + self._engine.random_digits_str(9)
|
|
69
|
+
return body + _ean_check_digit(body)
|
|
70
|
+
|
|
71
|
+
def _one_isbn10(self) -> str:
|
|
72
|
+
body = self._engine.random_digits_str(9)
|
|
73
|
+
return body + _isbn10_check_digit(body)
|
|
74
|
+
|
|
75
|
+
# ------------------------------------------------------------------
|
|
76
|
+
# Public API
|
|
77
|
+
# ------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
def ean13(self) -> str: ...
|
|
81
|
+
@overload
|
|
82
|
+
def ean13(self, count: Literal[1]) -> str: ...
|
|
83
|
+
@overload
|
|
84
|
+
def ean13(self, count: int) -> str | list[str]: ...
|
|
85
|
+
def ean13(self, count: int = 1) -> str | list[str]:
|
|
86
|
+
"""Generate a random EAN-13 barcode (13 digits, valid check digit).
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
count : int
|
|
91
|
+
Number of barcodes to generate.
|
|
92
|
+
"""
|
|
93
|
+
if count == 1:
|
|
94
|
+
return self._one_ean13()
|
|
95
|
+
return [self._one_ean13() for _ in range(count)]
|
|
96
|
+
|
|
97
|
+
@overload
|
|
98
|
+
def ean8(self) -> str: ...
|
|
99
|
+
@overload
|
|
100
|
+
def ean8(self, count: Literal[1]) -> str: ...
|
|
101
|
+
@overload
|
|
102
|
+
def ean8(self, count: int) -> str | list[str]: ...
|
|
103
|
+
def ean8(self, count: int = 1) -> str | list[str]:
|
|
104
|
+
"""Generate a random EAN-8 barcode (8 digits, valid check digit).
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
count : int
|
|
109
|
+
Number of barcodes to generate.
|
|
110
|
+
"""
|
|
111
|
+
if count == 1:
|
|
112
|
+
return self._one_ean8()
|
|
113
|
+
return [self._one_ean8() for _ in range(count)]
|
|
114
|
+
|
|
115
|
+
@overload
|
|
116
|
+
def isbn13(self) -> str: ...
|
|
117
|
+
@overload
|
|
118
|
+
def isbn13(self, count: Literal[1]) -> str: ...
|
|
119
|
+
@overload
|
|
120
|
+
def isbn13(self, count: int) -> str | list[str]: ...
|
|
121
|
+
def isbn13(self, count: int = 1) -> str | list[str]:
|
|
122
|
+
"""Generate a random ISBN-13 (starts with 978/979, valid check digit).
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
count : int
|
|
127
|
+
Number of ISBNs to generate.
|
|
128
|
+
"""
|
|
129
|
+
if count == 1:
|
|
130
|
+
return self._one_isbn13()
|
|
131
|
+
return [self._one_isbn13() for _ in range(count)]
|
|
132
|
+
|
|
133
|
+
@overload
|
|
134
|
+
def isbn10(self) -> str: ...
|
|
135
|
+
@overload
|
|
136
|
+
def isbn10(self, count: Literal[1]) -> str: ...
|
|
137
|
+
@overload
|
|
138
|
+
def isbn10(self, count: int) -> str | list[str]: ...
|
|
139
|
+
def isbn10(self, count: int = 1) -> str | list[str]:
|
|
140
|
+
"""Generate a random ISBN-10 (9 digits + check character).
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
count : int
|
|
145
|
+
Number of ISBNs to generate.
|
|
146
|
+
"""
|
|
147
|
+
if count == 1:
|
|
148
|
+
return self._one_isbn10()
|
|
149
|
+
return [self._one_isbn10() for _ in range(count)]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Base provider — shared foundation for all data providers."""
|
|
2
|
+
|
|
3
|
+
from dataforge.backend import RandomEngine
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseProvider:
|
|
7
|
+
"""Abstract base for all dataforge providers.
|
|
8
|
+
|
|
9
|
+
Holds a reference to the shared :class:`RandomEngine` so every
|
|
10
|
+
provider can generate random values without owning its own RNG
|
|
11
|
+
state.
|
|
12
|
+
|
|
13
|
+
Subclasses should define class-level metadata for the provider
|
|
14
|
+
registry:
|
|
15
|
+
|
|
16
|
+
- ``_provider_name``: short name used as attribute on ``DataForge``
|
|
17
|
+
(e.g. ``"person"``, ``"address"``).
|
|
18
|
+
- ``_locale_modules``: tuple of locale module names needed to
|
|
19
|
+
construct this provider (e.g. ``("person",)``). Empty tuple
|
|
20
|
+
``()`` for locale-independent providers.
|
|
21
|
+
- ``_field_map``: dict mapping shorthand field names to method
|
|
22
|
+
names (e.g. ``{"first_name": "first_name", "name": "full_name"}``).
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__slots__ = ("_engine",)
|
|
26
|
+
|
|
27
|
+
# Registry metadata — subclasses override these
|
|
28
|
+
_provider_name: str = ""
|
|
29
|
+
_locale_modules: tuple[str, ...] = ()
|
|
30
|
+
_field_map: dict[str, str] = {}
|
|
31
|
+
_needs_forge: bool = False
|
|
32
|
+
|
|
33
|
+
def __init__(self, engine: RandomEngine) -> None:
|
|
34
|
+
self._engine = engine
|