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.
Files changed (145) hide show
  1. dataforge/__init__.py +20 -0
  2. dataforge/backend.py +147 -0
  3. dataforge/cli.py +166 -0
  4. dataforge/core.py +1169 -0
  5. dataforge/locales/__init__.py +1 -0
  6. dataforge/locales/ar_SA/__init__.py +1 -0
  7. dataforge/locales/ar_SA/address.py +128 -0
  8. dataforge/locales/ar_SA/company.py +183 -0
  9. dataforge/locales/ar_SA/internet.py +25 -0
  10. dataforge/locales/ar_SA/person.py +217 -0
  11. dataforge/locales/ar_SA/phone.py +15 -0
  12. dataforge/locales/de_DE/__init__.py +1 -0
  13. dataforge/locales/de_DE/address.py +148 -0
  14. dataforge/locales/de_DE/company.py +125 -0
  15. dataforge/locales/de_DE/internet.py +32 -0
  16. dataforge/locales/de_DE/person.py +212 -0
  17. dataforge/locales/de_DE/phone.py +17 -0
  18. dataforge/locales/en_AU/__init__.py +1 -0
  19. dataforge/locales/en_AU/address.py +231 -0
  20. dataforge/locales/en_AU/company.py +193 -0
  21. dataforge/locales/en_AU/internet.py +34 -0
  22. dataforge/locales/en_AU/person.py +370 -0
  23. dataforge/locales/en_AU/phone.py +16 -0
  24. dataforge/locales/en_CA/__init__.py +1 -0
  25. dataforge/locales/en_CA/address.py +276 -0
  26. dataforge/locales/en_CA/company.py +193 -0
  27. dataforge/locales/en_CA/internet.py +34 -0
  28. dataforge/locales/en_CA/person.py +377 -0
  29. dataforge/locales/en_CA/phone.py +15 -0
  30. dataforge/locales/en_GB/__init__.py +1 -0
  31. dataforge/locales/en_GB/address.py +312 -0
  32. dataforge/locales/en_GB/company.py +196 -0
  33. dataforge/locales/en_GB/internet.py +34 -0
  34. dataforge/locales/en_GB/person.py +372 -0
  35. dataforge/locales/en_GB/phone.py +15 -0
  36. dataforge/locales/en_US/__init__.py +1 -0
  37. dataforge/locales/en_US/address.py +268 -0
  38. dataforge/locales/en_US/company.py +191 -0
  39. dataforge/locales/en_US/internet.py +34 -0
  40. dataforge/locales/en_US/person.py +370 -0
  41. dataforge/locales/en_US/phone.py +15 -0
  42. dataforge/locales/es_ES/__init__.py +1 -0
  43. dataforge/locales/es_ES/address.py +151 -0
  44. dataforge/locales/es_ES/company.py +125 -0
  45. dataforge/locales/es_ES/internet.py +30 -0
  46. dataforge/locales/es_ES/person.py +207 -0
  47. dataforge/locales/es_ES/phone.py +15 -0
  48. dataforge/locales/fr_FR/__init__.py +1 -0
  49. dataforge/locales/fr_FR/address.py +145 -0
  50. dataforge/locales/fr_FR/company.py +125 -0
  51. dataforge/locales/fr_FR/internet.py +30 -0
  52. dataforge/locales/fr_FR/person.py +212 -0
  53. dataforge/locales/fr_FR/phone.py +15 -0
  54. dataforge/locales/hi_IN/__init__.py +1 -0
  55. dataforge/locales/hi_IN/address.py +177 -0
  56. dataforge/locales/hi_IN/company.py +191 -0
  57. dataforge/locales/hi_IN/internet.py +26 -0
  58. dataforge/locales/hi_IN/person.py +218 -0
  59. dataforge/locales/hi_IN/phone.py +21 -0
  60. dataforge/locales/it_IT/__init__.py +1 -0
  61. dataforge/locales/it_IT/address.py +218 -0
  62. dataforge/locales/it_IT/company.py +151 -0
  63. dataforge/locales/it_IT/internet.py +31 -0
  64. dataforge/locales/it_IT/person.py +187 -0
  65. dataforge/locales/it_IT/phone.py +15 -0
  66. dataforge/locales/ja_JP/__init__.py +1 -0
  67. dataforge/locales/ja_JP/address.py +174 -0
  68. dataforge/locales/ja_JP/company.py +121 -0
  69. dataforge/locales/ja_JP/internet.py +30 -0
  70. dataforge/locales/ja_JP/person.py +207 -0
  71. dataforge/locales/ja_JP/phone.py +18 -0
  72. dataforge/locales/ko_KR/__init__.py +1 -0
  73. dataforge/locales/ko_KR/address.py +121 -0
  74. dataforge/locales/ko_KR/company.py +151 -0
  75. dataforge/locales/ko_KR/internet.py +30 -0
  76. dataforge/locales/ko_KR/person.py +157 -0
  77. dataforge/locales/ko_KR/phone.py +26 -0
  78. dataforge/locales/nl_NL/__init__.py +1 -0
  79. dataforge/locales/nl_NL/address.py +152 -0
  80. dataforge/locales/nl_NL/company.py +182 -0
  81. dataforge/locales/nl_NL/internet.py +41 -0
  82. dataforge/locales/nl_NL/person.py +218 -0
  83. dataforge/locales/nl_NL/phone.py +19 -0
  84. dataforge/locales/pl_PL/__init__.py +1 -0
  85. dataforge/locales/pl_PL/address.py +140 -0
  86. dataforge/locales/pl_PL/company.py +183 -0
  87. dataforge/locales/pl_PL/internet.py +36 -0
  88. dataforge/locales/pl_PL/person.py +217 -0
  89. dataforge/locales/pl_PL/phone.py +15 -0
  90. dataforge/locales/pt_BR/__init__.py +1 -0
  91. dataforge/locales/pt_BR/address.py +127 -0
  92. dataforge/locales/pt_BR/company.py +151 -0
  93. dataforge/locales/pt_BR/internet.py +31 -0
  94. dataforge/locales/pt_BR/person.py +187 -0
  95. dataforge/locales/pt_BR/phone.py +15 -0
  96. dataforge/locales/ru_RU/__init__.py +1 -0
  97. dataforge/locales/ru_RU/address.py +156 -0
  98. dataforge/locales/ru_RU/company.py +168 -0
  99. dataforge/locales/ru_RU/internet.py +26 -0
  100. dataforge/locales/ru_RU/person.py +218 -0
  101. dataforge/locales/ru_RU/phone.py +16 -0
  102. dataforge/locales/zh_CN/__init__.py +1 -0
  103. dataforge/locales/zh_CN/address.py +141 -0
  104. dataforge/locales/zh_CN/company.py +151 -0
  105. dataforge/locales/zh_CN/internet.py +30 -0
  106. dataforge/locales/zh_CN/person.py +157 -0
  107. dataforge/locales/zh_CN/phone.py +25 -0
  108. dataforge/providers/__init__.py +1 -0
  109. dataforge/providers/address.py +460 -0
  110. dataforge/providers/ai_chat.py +170 -0
  111. dataforge/providers/ai_prompt.py +447 -0
  112. dataforge/providers/automotive.py +416 -0
  113. dataforge/providers/barcode.py +149 -0
  114. dataforge/providers/base.py +34 -0
  115. dataforge/providers/color.py +247 -0
  116. dataforge/providers/company.py +144 -0
  117. dataforge/providers/crypto.py +105 -0
  118. dataforge/providers/datetime.py +397 -0
  119. dataforge/providers/ecommerce.py +316 -0
  120. dataforge/providers/education.py +234 -0
  121. dataforge/providers/file.py +271 -0
  122. dataforge/providers/finance.py +545 -0
  123. dataforge/providers/geo.py +332 -0
  124. dataforge/providers/government.py +114 -0
  125. dataforge/providers/internet.py +351 -0
  126. dataforge/providers/llm.py +726 -0
  127. dataforge/providers/lorem.py +241 -0
  128. dataforge/providers/medical.py +364 -0
  129. dataforge/providers/misc.py +196 -0
  130. dataforge/providers/network.py +283 -0
  131. dataforge/providers/payment.py +300 -0
  132. dataforge/providers/person.py +195 -0
  133. dataforge/providers/phone.py +87 -0
  134. dataforge/providers/profile.py +265 -0
  135. dataforge/providers/science.py +365 -0
  136. dataforge/providers/text.py +365 -0
  137. dataforge/py.typed +0 -0
  138. dataforge/pytest_plugin.py +80 -0
  139. dataforge/registry.py +164 -0
  140. dataforge/schema.py +772 -0
  141. dataforge/unique.py +171 -0
  142. dataforge_py-0.2.0.dist-info/METADATA +964 -0
  143. dataforge_py-0.2.0.dist-info/RECORD +145 -0
  144. dataforge_py-0.2.0.dist-info/WHEEL +4 -0
  145. 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