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,545 @@
1
+ """Finance provider — generates fake credit card numbers, IBANs, currencies."""
2
+
3
+ from typing import Literal, overload
4
+
5
+ from dataforge.providers.base import BaseProvider
6
+
7
+ # Credit card prefixes by network (BIN ranges)
8
+ _CARD_TYPES: tuple[tuple[str, str, int], ...] = (
9
+ # (name, prefix, total_length)
10
+ ("Visa", "4", 16),
11
+ ("Visa", "4", 16),
12
+ ("Visa", "4", 16),
13
+ ("Mastercard", "51", 16),
14
+ ("Mastercard", "52", 16),
15
+ ("Mastercard", "53", 16),
16
+ ("Mastercard", "54", 16),
17
+ ("Mastercard", "55", 16),
18
+ ("American Express", "34", 15),
19
+ ("American Express", "37", 15),
20
+ ("Discover", "6011", 16),
21
+ ("Discover", "65", 16),
22
+ )
23
+
24
+ _CURRENCIES: tuple[tuple[str, str, str], ...] = (
25
+ # (code, name, symbol)
26
+ ("USD", "US Dollar", "$"),
27
+ ("EUR", "Euro", "\u20ac"),
28
+ ("GBP", "British Pound", "\u00a3"),
29
+ ("JPY", "Japanese Yen", "\u00a5"),
30
+ ("CNY", "Chinese Yuan", "\u00a5"),
31
+ ("KRW", "South Korean Won", "\u20a9"),
32
+ ("BRL", "Brazilian Real", "R$"),
33
+ ("CAD", "Canadian Dollar", "C$"),
34
+ ("AUD", "Australian Dollar", "A$"),
35
+ ("CHF", "Swiss Franc", "CHF"),
36
+ ("INR", "Indian Rupee", "\u20b9"),
37
+ ("MXN", "Mexican Peso", "$"),
38
+ ("SEK", "Swedish Krona", "kr"),
39
+ ("NOK", "Norwegian Krone", "kr"),
40
+ ("DKK", "Danish Krone", "kr"),
41
+ ("PLN", "Polish Zloty", "z\u0142"),
42
+ ("TRY", "Turkish Lira", "\u20ba"),
43
+ ("RUB", "Russian Ruble", "\u20bd"),
44
+ ("ZAR", "South African Rand", "R"),
45
+ ("NZD", "New Zealand Dollar", "NZ$"),
46
+ )
47
+
48
+ # Pre-split parallel tuples for vectorized batch generation —
49
+ # avoids per-item tuple indexing overhead in batch paths.
50
+ _CURRENCY_CODES: tuple[str, ...] = tuple(c[0] for c in _CURRENCIES)
51
+ _CURRENCY_NAMES: tuple[str, ...] = tuple(c[1] for c in _CURRENCIES)
52
+ _CURRENCY_SYMBOLS: tuple[str, ...] = tuple(c[2] for c in _CURRENCIES)
53
+
54
+ # IBAN formats by country: (country_code, total_length)
55
+ _IBAN_FORMATS: tuple[tuple[str, int], ...] = (
56
+ ("DE", 22),
57
+ ("FR", 27),
58
+ ("GB", 22),
59
+ ("ES", 24),
60
+ ("IT", 27),
61
+ ("NL", 18),
62
+ ("BE", 16),
63
+ ("AT", 20),
64
+ ("CH", 21),
65
+ ("PT", 25),
66
+ )
67
+
68
+ # BIC/SWIFT code components — pre-computed 4-char bank codes at module level
69
+ # to avoid per-call ``[:4].ljust(4, "X")`` overhead.
70
+ _BIC_BANK_CODES: tuple[str, ...] = tuple(
71
+ b[:4].ljust(4, "X")
72
+ for b in (
73
+ "DEUTDEFF",
74
+ "BNPAFRPP",
75
+ "BARCGB22",
76
+ "CHASUS33",
77
+ "CITIUS33",
78
+ "COBADEFF",
79
+ "HSBC",
80
+ "INGB",
81
+ "SCBL",
82
+ "UBSW",
83
+ "ABNA",
84
+ "RABO",
85
+ "BOFAUS3N",
86
+ "WFBIUS6S",
87
+ "NWBKGB2L",
88
+ "LOYDGB21",
89
+ "BKENGB2L",
90
+ "SOGEFRPP",
91
+ "CRLYFRPP",
92
+ "AGRIFRPP",
93
+ )
94
+ )
95
+
96
+ _BIC_LOCATIONS: tuple[str, ...] = (
97
+ "FF",
98
+ "PP",
99
+ "22",
100
+ "33",
101
+ "2L",
102
+ "3N",
103
+ "6S",
104
+ "MM",
105
+ "XX",
106
+ "HH",
107
+ "LX",
108
+ "BB",
109
+ "CC",
110
+ "DD",
111
+ "EE",
112
+ "GG",
113
+ "KK",
114
+ "LL",
115
+ "SS",
116
+ "TT",
117
+ )
118
+
119
+ _BIC_COUNTRIES: tuple[str, ...] = (
120
+ "DE",
121
+ "FR",
122
+ "GB",
123
+ "US",
124
+ "NL",
125
+ "CH",
126
+ "IT",
127
+ "ES",
128
+ "AT",
129
+ "BE",
130
+ )
131
+
132
+ # Base58 alphabet for Bitcoin addresses (as a string for O(1) indexing)
133
+ _BASE58_STR: str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
134
+ _BASE58_LEN: int = 58 # len(_BASE58_STR)
135
+
136
+ # 7h — Luhn doubling lookup table: eliminates per-digit ``if d > 9: d -= 9``
137
+ _LUHN_DOUBLE: tuple[int, ...] = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
138
+
139
+
140
+ _CARD_TYPE_NAMES: tuple[str, ...] = (
141
+ "Visa",
142
+ "Mastercard",
143
+ "American Express",
144
+ "Discover",
145
+ )
146
+
147
+
148
+ def _luhn_checksum(number: str) -> str:
149
+ """Append a Luhn check digit to *number* and return the full card number.
150
+
151
+ Uses reverse index math to avoid creating a ``reversed()`` iterator,
152
+ and ``ord(ch) - 48`` with a pre-computed ``_LUHN_DOUBLE`` lookup
153
+ table to eliminate per-digit branching.
154
+ """
155
+ total = 0
156
+ _double = _LUHN_DOUBLE
157
+ n_len = len(number)
158
+ for i in range(n_len):
159
+ d = ord(number[n_len - 1 - i]) - 48
160
+ if i % 2 == 0:
161
+ d = _double[d]
162
+ total += d
163
+ check = (10 - (total % 10)) % 10
164
+ return number + str(check)
165
+
166
+
167
+ class FinanceProvider(BaseProvider):
168
+ """Generates fake financial data: credit cards, IBANs, currencies.
169
+
170
+ Parameters
171
+ ----------
172
+ engine : RandomEngine
173
+ The shared random engine instance.
174
+ """
175
+
176
+ __slots__ = ()
177
+
178
+ _provider_name = "finance"
179
+ _locale_modules = ()
180
+ _field_map = {
181
+ "credit_card_number": "credit_card_number",
182
+ "card_number": "credit_card_number",
183
+ "card_type": "card_type",
184
+ "iban": "iban",
185
+ "currency_code": "currency_code",
186
+ "currency": "currency_code",
187
+ "currency_name": "currency_name",
188
+ "currency_symbol": "currency_symbol",
189
+ "price": "price",
190
+ "bic": "bic",
191
+ "routing_number": "routing_number",
192
+ "bitcoin_address": "bitcoin_address",
193
+ }
194
+
195
+ # ------------------------------------------------------------------
196
+ # Scalar helpers
197
+ # ------------------------------------------------------------------
198
+
199
+ def _one_credit_card_number(self) -> str:
200
+ _, prefix, length = self._engine.choice(_CARD_TYPES)
201
+ # Generate remaining digits (minus prefix, minus check digit)
202
+ remaining = length - len(prefix) - 1
203
+ body = prefix + self._engine.random_digits_str(remaining)
204
+ return _luhn_checksum(body)
205
+
206
+ def _one_credit_card(self) -> dict[str, str]:
207
+ name, prefix, length = self._engine.choice(_CARD_TYPES)
208
+ remaining = length - len(prefix) - 1
209
+ body = prefix + self._engine.random_digits_str(remaining)
210
+ number = _luhn_checksum(body)
211
+ exp_month = str(self._engine.random_int(1, 12)).zfill(2)
212
+ exp_year = str(self._engine.random_int(25, 30))
213
+ cvv_len = 4 if name == "American Express" else 3
214
+ cvv = self._engine.random_digits_str(cvv_len)
215
+ return {
216
+ "type": name,
217
+ "number": number,
218
+ "exp": f"{exp_month}/{exp_year}",
219
+ "cvv": cvv,
220
+ }
221
+
222
+ def _one_iban(self) -> str:
223
+ country, length = self._engine.choice(_IBAN_FORMATS)
224
+ bban_len = length - 4 # 2 country + 2 check
225
+ check = str(self._engine.random_int(2, 98)).zfill(2)
226
+ bban = self._engine.random_digits_str(bban_len)
227
+ return f"{country}{check}{bban}"
228
+
229
+ def _one_price(self, min_cents: int, max_cents: int) -> str:
230
+ cents = self._engine.random_int(min_cents, max_cents)
231
+ return f"{cents / 100:.2f}"
232
+
233
+ def _one_bic(self) -> str:
234
+ bank_code = self._engine.choice(_BIC_BANK_CODES)
235
+ country = self._engine.choice(_BIC_COUNTRIES)
236
+ location = self._engine.choice(_BIC_LOCATIONS)
237
+ return f"{bank_code}{country}{location}XXX"
238
+
239
+ def _one_routing_number(self) -> str:
240
+ # First two digits: Federal Reserve district (01-12)
241
+ d1 = self._engine.random_int(0, 1)
242
+ d2 = self._engine.random_int(1, 2) if d1 == 1 else self._engine.random_int(1, 9)
243
+ # Generate 6 random digits in one call
244
+ mid = self._engine.random_digits_str(6)
245
+ d = [
246
+ d1,
247
+ d2,
248
+ ord(mid[0]) - 48,
249
+ ord(mid[1]) - 48,
250
+ ord(mid[2]) - 48,
251
+ ord(mid[3]) - 48,
252
+ ord(mid[4]) - 48,
253
+ ord(mid[5]) - 48,
254
+ ]
255
+ # ABA checksum
256
+ total = (
257
+ 3 * d[0]
258
+ + 7 * d[1]
259
+ + d[2]
260
+ + 3 * d[3]
261
+ + 7 * d[4]
262
+ + d[5]
263
+ + 3 * d[6]
264
+ + 7 * d[7]
265
+ )
266
+ check = (10 - (total % 10)) % 10
267
+ return f"{d1}{d2}{mid}{check}"
268
+
269
+ def _one_bitcoin_address(self) -> str:
270
+ # P2PKH addresses: "1" + 25-33 Base58 characters
271
+ length = self._engine.random_int(25, 33)
272
+ # 7k — Use divmod(bits, 58) for unbiased Base58 encoding.
273
+ # Previous approach used ``bits % 58`` + ``bits >>= 6`` which
274
+ # was biased because 2^6 = 64 is not divisible by 58.
275
+ bits = self._engine.getrandbits(length * 6)
276
+ chars: list[str] = []
277
+ for _ in range(length):
278
+ bits, idx = divmod(bits, _BASE58_LEN)
279
+ chars.append(_BASE58_STR[idx])
280
+ return "1" + "".join(chars)
281
+
282
+ # ------------------------------------------------------------------
283
+ # Public API
284
+ # ------------------------------------------------------------------
285
+
286
+ @overload
287
+ def credit_card_number(self) -> str: ...
288
+ @overload
289
+ def credit_card_number(self, count: Literal[1]) -> str: ...
290
+ @overload
291
+ def credit_card_number(self, count: int) -> str | list[str]: ...
292
+ def credit_card_number(self, count: int = 1) -> str | list[str]:
293
+ """Generate a random credit card number (Luhn-valid).
294
+
295
+ Parameters
296
+ ----------
297
+ count : int
298
+ Number of card numbers to generate.
299
+ """
300
+ if count == 1:
301
+ return self._one_credit_card_number()
302
+ return [self._one_credit_card_number() for _ in range(count)]
303
+
304
+ @overload
305
+ def credit_card(self) -> dict[str, str]: ...
306
+ @overload
307
+ def credit_card(self, count: Literal[1]) -> dict[str, str]: ...
308
+ @overload
309
+ def credit_card(self, count: int) -> dict[str, str] | list[dict[str, str]]: ...
310
+ def credit_card(self, count: int = 1) -> dict[str, str] | list[dict[str, str]]:
311
+ """Generate a full credit card (number, type, expiry, CVV).
312
+
313
+ Parameters
314
+ ----------
315
+ count : int
316
+ Number of cards to generate.
317
+ """
318
+ if count == 1:
319
+ return self._one_credit_card()
320
+ return [self._one_credit_card() for _ in range(count)]
321
+
322
+ @overload
323
+ def card_type(self) -> str: ...
324
+ @overload
325
+ def card_type(self, count: Literal[1]) -> str: ...
326
+ @overload
327
+ def card_type(self, count: int) -> str | list[str]: ...
328
+ def card_type(self, count: int = 1) -> str | list[str]:
329
+ """Generate a random credit card network name.
330
+
331
+ Parameters
332
+ ----------
333
+ count : int
334
+ Number of card types to generate.
335
+ """
336
+ types = _CARD_TYPE_NAMES
337
+ if count == 1:
338
+ return self._engine.choice(types)
339
+ return self._engine.choices(types, count)
340
+
341
+ @overload
342
+ def iban(self) -> str: ...
343
+ @overload
344
+ def iban(self, count: Literal[1]) -> str: ...
345
+ @overload
346
+ def iban(self, count: int) -> str | list[str]: ...
347
+ def iban(self, count: int = 1) -> str | list[str]:
348
+ """Generate a random IBAN.
349
+
350
+ Parameters
351
+ ----------
352
+ count : int
353
+ Number of IBANs to generate.
354
+ """
355
+ if count == 1:
356
+ return self._one_iban()
357
+ return [self._one_iban() for _ in range(count)]
358
+
359
+ @overload
360
+ def currency_code(self) -> str: ...
361
+ @overload
362
+ def currency_code(self, count: Literal[1]) -> str: ...
363
+ @overload
364
+ def currency_code(self, count: int) -> str | list[str]: ...
365
+ def currency_code(self, count: int = 1) -> str | list[str]:
366
+ """Generate a random ISO 4217 currency code (e.g. ``"USD"``).
367
+
368
+ Parameters
369
+ ----------
370
+ count : int
371
+ Number of codes to generate.
372
+ """
373
+ if count == 1:
374
+ return self._engine.choice(_CURRENCY_CODES)
375
+ return self._engine.choices(_CURRENCY_CODES, count)
376
+
377
+ @overload
378
+ def currency_name(self) -> str: ...
379
+ @overload
380
+ def currency_name(self, count: Literal[1]) -> str: ...
381
+ @overload
382
+ def currency_name(self, count: int) -> str | list[str]: ...
383
+ def currency_name(self, count: int = 1) -> str | list[str]:
384
+ """Generate a random currency name (e.g. ``"US Dollar"``).
385
+
386
+ Parameters
387
+ ----------
388
+ count : int
389
+ Number of names to generate.
390
+ """
391
+ if count == 1:
392
+ return self._engine.choice(_CURRENCY_NAMES)
393
+ return self._engine.choices(_CURRENCY_NAMES, count)
394
+
395
+ @overload
396
+ def currency_symbol(self) -> str: ...
397
+ @overload
398
+ def currency_symbol(self, count: Literal[1]) -> str: ...
399
+ @overload
400
+ def currency_symbol(self, count: int) -> str | list[str]: ...
401
+ def currency_symbol(self, count: int = 1) -> str | list[str]:
402
+ """Generate a random currency symbol (e.g. ``"$"``).
403
+
404
+ Parameters
405
+ ----------
406
+ count : int
407
+ Number of symbols to generate.
408
+ """
409
+ if count == 1:
410
+ return self._engine.choice(_CURRENCY_SYMBOLS)
411
+ return self._engine.choices(_CURRENCY_SYMBOLS, count)
412
+
413
+ @overload
414
+ def price(self) -> str: ...
415
+ @overload
416
+ def price(self, count: Literal[1]) -> str: ...
417
+ @overload
418
+ def price(self, count: int) -> str | list[str]: ...
419
+ def price(
420
+ self, count: int = 1, min_val: float = 0.99, max_val: float = 9999.99
421
+ ) -> str | list[str]:
422
+ """Generate a random price string (e.g. ``"49.99"``).
423
+
424
+ Parameters
425
+ ----------
426
+ count : int
427
+ Number of prices to generate.
428
+ min_val : float
429
+ Minimum price value.
430
+ max_val : float
431
+ Maximum price value.
432
+ """
433
+ min_cents = int(min_val * 100)
434
+ max_cents = int(max_val * 100)
435
+ if count == 1:
436
+ return self._one_price(min_cents, max_cents)
437
+ # Inlined batch loop with local-bound random_int
438
+ _ri = self._engine.random_int
439
+ return [f"{_ri(min_cents, max_cents) / 100:.2f}" for _ in range(count)]
440
+
441
+ @overload
442
+ def bic(self) -> str: ...
443
+ @overload
444
+ def bic(self, count: Literal[1]) -> str: ...
445
+ @overload
446
+ def bic(self, count: int) -> str | list[str]: ...
447
+ def bic(self, count: int = 1) -> str | list[str]:
448
+ """Generate a random BIC/SWIFT code (e.g. ``"DEUTDEFFXXX"``).
449
+
450
+ Parameters
451
+ ----------
452
+ count : int
453
+ Number of BIC codes to generate.
454
+ """
455
+ if count == 1:
456
+ return self._one_bic()
457
+ # Inlined batch loop with local-bound choices
458
+ _choice = self._engine.choice
459
+ return [
460
+ f"{_choice(_BIC_BANK_CODES)}{_choice(_BIC_COUNTRIES)}{_choice(_BIC_LOCATIONS)}XXX"
461
+ for _ in range(count)
462
+ ]
463
+
464
+ @overload
465
+ def routing_number(self) -> str: ...
466
+ @overload
467
+ def routing_number(self, count: Literal[1]) -> str: ...
468
+ @overload
469
+ def routing_number(self, count: int) -> str | list[str]: ...
470
+ def routing_number(self, count: int = 1) -> str | list[str]:
471
+ """Generate a random US ABA routing number with valid checksum.
472
+
473
+ Parameters
474
+ ----------
475
+ count : int
476
+ Number of routing numbers to generate.
477
+ """
478
+ if count == 1:
479
+ return self._one_routing_number()
480
+ # Inlined batch with local-bound helpers
481
+ _ri = self._engine.random_int
482
+ _rds = self._engine.random_digits_str
483
+ _ord = ord
484
+ result: list[str] = []
485
+ for _ in range(count):
486
+ d1 = _ri(0, 1)
487
+ d2 = _ri(1, 2) if d1 == 1 else _ri(1, 9)
488
+ mid = _rds(6)
489
+ d = [
490
+ d1,
491
+ d2,
492
+ _ord(mid[0]) - 48,
493
+ _ord(mid[1]) - 48,
494
+ _ord(mid[2]) - 48,
495
+ _ord(mid[3]) - 48,
496
+ _ord(mid[4]) - 48,
497
+ _ord(mid[5]) - 48,
498
+ ]
499
+ total = (
500
+ 3 * d[0]
501
+ + 7 * d[1]
502
+ + d[2]
503
+ + 3 * d[3]
504
+ + 7 * d[4]
505
+ + d[5]
506
+ + 3 * d[6]
507
+ + 7 * d[7]
508
+ )
509
+ check = (10 - (total % 10)) % 10
510
+ result.append(f"{d1}{d2}{mid}{check}")
511
+ return result
512
+
513
+ @overload
514
+ def bitcoin_address(self) -> str: ...
515
+ @overload
516
+ def bitcoin_address(self, count: Literal[1]) -> str: ...
517
+ @overload
518
+ def bitcoin_address(self, count: int) -> str | list[str]: ...
519
+ def bitcoin_address(self, count: int = 1) -> str | list[str]:
520
+ """Generate a random Bitcoin address (P2PKH format, starts with ``1``).
521
+
522
+ Parameters
523
+ ----------
524
+ count : int
525
+ Number of addresses to generate.
526
+ """
527
+ if count == 1:
528
+ return self._one_bitcoin_address()
529
+ # Inlined batch loop — use modular indexing into BASE58 alphabet
530
+ _b58 = _BASE58_STR
531
+ _b58_len = _BASE58_LEN
532
+ _getrandbits = self._engine.getrandbits
533
+ _ri = self._engine.random_int
534
+ result: list[str] = []
535
+ for _i in range(count):
536
+ length = _ri(25, 33)
537
+ # Generate all random bits at once
538
+ bits = _getrandbits(length * 6)
539
+ # Build string using list comprehension with divmod unrolled
540
+ chars: list[str] = ["1"]
541
+ for _j in range(length):
542
+ chars.append(_b58[bits % _b58_len])
543
+ bits //= _b58_len
544
+ result.append("".join(chars))
545
+ return result