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,300 @@
1
+ """Payment provider — credit card types, payment methods, processors, etc."""
2
+
3
+ from typing import Literal, overload
4
+
5
+ from dataforge.providers.base import BaseProvider
6
+
7
+ _CARD_TYPES: tuple[str, ...] = (
8
+ "Visa",
9
+ "Mastercard",
10
+ "American Express",
11
+ "Discover",
12
+ "Diners Club",
13
+ "JCB",
14
+ "UnionPay",
15
+ "Maestro",
16
+ "Mir",
17
+ "Elo",
18
+ )
19
+
20
+ _PAYMENT_METHODS: tuple[str, ...] = (
21
+ "Credit Card",
22
+ "Debit Card",
23
+ "PayPal",
24
+ "Apple Pay",
25
+ "Google Pay",
26
+ "Samsung Pay",
27
+ "Bank Transfer",
28
+ "Wire Transfer",
29
+ "Cash",
30
+ "Check",
31
+ "Cryptocurrency",
32
+ "Venmo",
33
+ "Zelle",
34
+ "Alipay",
35
+ "WeChat Pay",
36
+ "Klarna",
37
+ "Afterpay",
38
+ "Cash App",
39
+ "Money Order",
40
+ "ACH Transfer",
41
+ )
42
+
43
+ _PROCESSORS: tuple[str, ...] = (
44
+ "Stripe",
45
+ "Square",
46
+ "Adyen",
47
+ "Braintree",
48
+ "Worldpay",
49
+ "Checkout.com",
50
+ "PayPal Commerce",
51
+ "Authorize.Net",
52
+ "2Checkout",
53
+ "BlueSnap",
54
+ "Payoneer",
55
+ "Razorpay",
56
+ "Mollie",
57
+ "dLocal",
58
+ "Nuvei",
59
+ )
60
+
61
+ _TRANSACTION_STATUSES: tuple[str, ...] = (
62
+ "pending",
63
+ "processing",
64
+ "completed",
65
+ "failed",
66
+ "refunded",
67
+ "partially_refunded",
68
+ "voided",
69
+ "disputed",
70
+ "chargeback",
71
+ "authorized",
72
+ )
73
+
74
+ _CURRENCIES: tuple[str, ...] = (
75
+ "USD",
76
+ "EUR",
77
+ "GBP",
78
+ "JPY",
79
+ "CHF",
80
+ "CAD",
81
+ "AUD",
82
+ "CNY",
83
+ "HKD",
84
+ "NZD",
85
+ "SEK",
86
+ "NOK",
87
+ "DKK",
88
+ "SGD",
89
+ "KRW",
90
+ "INR",
91
+ "BRL",
92
+ "MXN",
93
+ "ZAR",
94
+ "PLN",
95
+ "TRY",
96
+ "RUB",
97
+ "THB",
98
+ "TWD",
99
+ "AED",
100
+ )
101
+
102
+ _CURRENCY_SYMBOLS: tuple[str, ...] = (
103
+ "$",
104
+ "€",
105
+ "£",
106
+ "¥",
107
+ "Fr",
108
+ "C$",
109
+ "A$",
110
+ "¥",
111
+ "HK$",
112
+ "NZ$",
113
+ "kr",
114
+ "kr",
115
+ "kr",
116
+ "S$",
117
+ "₩",
118
+ "₹",
119
+ "R$",
120
+ "MX$",
121
+ "R",
122
+ "zł",
123
+ "₺",
124
+ "₽",
125
+ "฿",
126
+ "NT$",
127
+ "د.إ",
128
+ )
129
+
130
+
131
+ class PaymentProvider(BaseProvider):
132
+ """Generates fake payment and transaction data."""
133
+
134
+ __slots__ = ()
135
+
136
+ _provider_name = "payment"
137
+ _locale_modules: tuple[str, ...] = ()
138
+ _field_map: dict[str, str] = {
139
+ "card_type": "card_type",
140
+ "payment_method": "payment_method",
141
+ "payment_processor": "payment_processor",
142
+ "processor": "payment_processor",
143
+ "transaction_status": "transaction_status",
144
+ "transaction_id": "transaction_id",
145
+ "txn_id": "transaction_id",
146
+ "currency_code": "currency_code",
147
+ "currency_symbol": "currency_symbol",
148
+ "payment_amount": "payment_amount",
149
+ "cvv": "cvv",
150
+ "expiry_date": "expiry_date",
151
+ "card_expiry": "expiry_date",
152
+ }
153
+
154
+ # --- Scalar helpers ---
155
+
156
+ def _one_transaction_id(self) -> str:
157
+ return f"TXN-{self._engine.random_digits_str(12)}"
158
+
159
+ def _one_payment_amount(self) -> str:
160
+ dollars = self._engine.random_int(1, 9999)
161
+ cents = self._engine.random_int(0, 99)
162
+ return f"{dollars}.{cents:02d}"
163
+
164
+ def _one_cvv(self) -> str:
165
+ return self._engine.random_digits_str(
166
+ 4 if self._engine.random_int(0, 1) == 0 else 3
167
+ )
168
+
169
+ def _one_expiry_date(self) -> str:
170
+ month = self._engine.random_int(1, 12)
171
+ year = self._engine.random_int(25, 32)
172
+ return f"{month:02d}/{year:02d}"
173
+
174
+ # --- Public API ---
175
+
176
+ @overload
177
+ def card_type(self) -> str: ...
178
+ @overload
179
+ def card_type(self, count: Literal[1]) -> str: ...
180
+ @overload
181
+ def card_type(self, count: int) -> str | list[str]: ...
182
+ def card_type(self, count: int = 1) -> str | list[str]:
183
+ """Generate a credit/debit card type (e.g., Visa, Mastercard)."""
184
+ if count == 1:
185
+ return self._engine.choice(_CARD_TYPES)
186
+ return self._engine.choices(_CARD_TYPES, count)
187
+
188
+ @overload
189
+ def payment_method(self) -> str: ...
190
+ @overload
191
+ def payment_method(self, count: Literal[1]) -> str: ...
192
+ @overload
193
+ def payment_method(self, count: int) -> str | list[str]: ...
194
+ def payment_method(self, count: int = 1) -> str | list[str]:
195
+ """Generate a payment method (e.g., Credit Card, PayPal)."""
196
+ if count == 1:
197
+ return self._engine.choice(_PAYMENT_METHODS)
198
+ return self._engine.choices(_PAYMENT_METHODS, count)
199
+
200
+ @overload
201
+ def payment_processor(self) -> str: ...
202
+ @overload
203
+ def payment_processor(self, count: Literal[1]) -> str: ...
204
+ @overload
205
+ def payment_processor(self, count: int) -> str | list[str]: ...
206
+ def payment_processor(self, count: int = 1) -> str | list[str]:
207
+ """Generate a payment processor name (e.g., Stripe, Square)."""
208
+ if count == 1:
209
+ return self._engine.choice(_PROCESSORS)
210
+ return self._engine.choices(_PROCESSORS, count)
211
+
212
+ @overload
213
+ def transaction_status(self) -> str: ...
214
+ @overload
215
+ def transaction_status(self, count: Literal[1]) -> str: ...
216
+ @overload
217
+ def transaction_status(self, count: int) -> str | list[str]: ...
218
+ def transaction_status(self, count: int = 1) -> str | list[str]:
219
+ """Generate a transaction status (e.g., pending, completed)."""
220
+ if count == 1:
221
+ return self._engine.choice(_TRANSACTION_STATUSES)
222
+ return self._engine.choices(_TRANSACTION_STATUSES, count)
223
+
224
+ @overload
225
+ def transaction_id(self) -> str: ...
226
+ @overload
227
+ def transaction_id(self, count: Literal[1]) -> str: ...
228
+ @overload
229
+ def transaction_id(self, count: int) -> str | list[str]: ...
230
+ def transaction_id(self, count: int = 1) -> str | list[str]:
231
+ """Generate a transaction ID (TXN-############)."""
232
+ if count == 1:
233
+ return self._one_transaction_id()
234
+ # Inlined batch with local-bound random_digits_str
235
+ _rds = self._engine.random_digits_str
236
+ return [f"TXN-{_rds(12)}" for _ in range(count)]
237
+
238
+ @overload
239
+ def currency_code(self) -> str: ...
240
+ @overload
241
+ def currency_code(self, count: Literal[1]) -> str: ...
242
+ @overload
243
+ def currency_code(self, count: int) -> str | list[str]: ...
244
+ def currency_code(self, count: int = 1) -> str | list[str]:
245
+ """Generate an ISO 4217 currency code (e.g., USD, EUR)."""
246
+ if count == 1:
247
+ return self._engine.choice(_CURRENCIES)
248
+ return self._engine.choices(_CURRENCIES, count)
249
+
250
+ @overload
251
+ def currency_symbol(self) -> str: ...
252
+ @overload
253
+ def currency_symbol(self, count: Literal[1]) -> str: ...
254
+ @overload
255
+ def currency_symbol(self, count: int) -> str | list[str]: ...
256
+ def currency_symbol(self, count: int = 1) -> str | list[str]:
257
+ """Generate a currency symbol (e.g., $, EUR, GBP)."""
258
+ if count == 1:
259
+ return self._engine.choice(_CURRENCY_SYMBOLS)
260
+ return self._engine.choices(_CURRENCY_SYMBOLS, count)
261
+
262
+ @overload
263
+ def payment_amount(self) -> str: ...
264
+ @overload
265
+ def payment_amount(self, count: Literal[1]) -> str: ...
266
+ @overload
267
+ def payment_amount(self, count: int) -> str | list[str]: ...
268
+ def payment_amount(self, count: int = 1) -> str | list[str]:
269
+ """Generate a payment amount (e.g., 49.99)."""
270
+ if count == 1:
271
+ return self._one_payment_amount()
272
+ _ri = self._engine.random_int
273
+ return [f"{_ri(1, 9999)}.{_ri(0, 99):02d}" for _ in range(count)]
274
+
275
+ @overload
276
+ def cvv(self) -> str: ...
277
+ @overload
278
+ def cvv(self, count: Literal[1]) -> str: ...
279
+ @overload
280
+ def cvv(self, count: int) -> str | list[str]: ...
281
+ def cvv(self, count: int = 1) -> str | list[str]:
282
+ """Generate a CVV code (3 or 4 digits)."""
283
+ if count == 1:
284
+ return self._one_cvv()
285
+ _rds = self._engine.random_digits_str
286
+ _ri = self._engine.random_int
287
+ return [_rds(4 if _ri(0, 1) == 0 else 3) for _ in range(count)]
288
+
289
+ @overload
290
+ def expiry_date(self) -> str: ...
291
+ @overload
292
+ def expiry_date(self, count: Literal[1]) -> str: ...
293
+ @overload
294
+ def expiry_date(self, count: int) -> str | list[str]: ...
295
+ def expiry_date(self, count: int = 1) -> str | list[str]:
296
+ """Generate a card expiry date (MM/YY)."""
297
+ if count == 1:
298
+ return self._one_expiry_date()
299
+ _ri = self._engine.random_int
300
+ return [f"{_ri(1, 12):02d}/{_ri(25, 32):02d}" for _ in range(count)]
@@ -0,0 +1,195 @@
1
+ """Person provider — generates fake personal names."""
2
+
3
+ from types import ModuleType
4
+ from typing import Literal, overload
5
+
6
+ from dataforge.backend import RandomEngine
7
+ from dataforge.providers.base import BaseProvider
8
+
9
+ # Module-level constants for zero per-call overhead
10
+ _PREFIXES: tuple[str, ...] = ("Mr.", "Mrs.", "Ms.", "Dr.")
11
+ _SUFFIXES: tuple[str, ...] = ("Jr.", "Sr.", "III", "IV", "V")
12
+
13
+
14
+ class PersonProvider(BaseProvider):
15
+ """Generates fake first names, last names, and full names.
16
+
17
+ Parameters
18
+ ----------
19
+ engine : RandomEngine
20
+ The shared random engine instance.
21
+ locale_data : ModuleType
22
+ The imported locale module (e.g. ``dataforge.locales.en_US.person``).
23
+ """
24
+
25
+ __slots__ = (
26
+ "_first_names",
27
+ "_last_names",
28
+ "_male_first_names",
29
+ "_female_first_names",
30
+ )
31
+
32
+ _provider_name = "person"
33
+ _locale_modules = ("person",)
34
+ _field_map = {
35
+ "first_name": "first_name",
36
+ "last_name": "last_name",
37
+ "full_name": "full_name",
38
+ "name": "full_name",
39
+ "prefix": "prefix",
40
+ "suffix": "suffix",
41
+ "male_first_name": "male_first_name",
42
+ "female_first_name": "female_first_name",
43
+ }
44
+
45
+ def __init__(self, engine: RandomEngine, locale_data: ModuleType) -> None:
46
+ super().__init__(engine)
47
+ self._first_names: tuple[str, ...] = locale_data.first_names
48
+ self._last_names: tuple[str, ...] = locale_data.last_names
49
+ # Gendered names — optional in locale data; fall back to full list
50
+ self._male_first_names: tuple[str, ...] = getattr(
51
+ locale_data, "male_first_names", locale_data.first_names
52
+ )
53
+ self._female_first_names: tuple[str, ...] = getattr(
54
+ locale_data, "female_first_names", locale_data.first_names
55
+ )
56
+
57
+ # ------------------------------------------------------------------
58
+ # Public API
59
+ # ------------------------------------------------------------------
60
+
61
+ @overload
62
+ def first_name(self) -> str: ...
63
+ @overload
64
+ def first_name(self, count: Literal[1]) -> str: ...
65
+ @overload
66
+ def first_name(self, count: int) -> str | list[str]: ...
67
+ def first_name(self, count: int = 1) -> str | list[str]:
68
+ """Generate a random first name.
69
+
70
+ Parameters
71
+ ----------
72
+ count : int
73
+ Number of names to generate. ``1`` returns a single ``str``;
74
+ any value > 1 returns a ``list[str]``.
75
+ """
76
+ if count == 1:
77
+ return self._engine.choice(self._first_names)
78
+ return self._engine.choices(self._first_names, count)
79
+
80
+ @overload
81
+ def last_name(self) -> str: ...
82
+ @overload
83
+ def last_name(self, count: Literal[1]) -> str: ...
84
+ @overload
85
+ def last_name(self, count: int) -> str | list[str]: ...
86
+ def last_name(self, count: int = 1) -> str | list[str]:
87
+ """Generate a random last name.
88
+
89
+ Parameters
90
+ ----------
91
+ count : int
92
+ Number of names to generate. ``1`` returns a single ``str``;
93
+ any value > 1 returns a ``list[str]``.
94
+ """
95
+ if count == 1:
96
+ return self._engine.choice(self._last_names)
97
+ return self._engine.choices(self._last_names, count)
98
+
99
+ @overload
100
+ def full_name(self) -> str: ...
101
+ @overload
102
+ def full_name(self, count: Literal[1]) -> str: ...
103
+ @overload
104
+ def full_name(self, count: int) -> str | list[str]: ...
105
+ def full_name(self, count: int = 1) -> str | list[str]:
106
+ """Generate a random full name (first + last).
107
+
108
+ Parameters
109
+ ----------
110
+ count : int
111
+ Number of names to generate. ``1`` returns a single ``str``;
112
+ any value > 1 returns a ``list[str]``.
113
+ """
114
+ if count == 1:
115
+ first = self._engine.choice(self._first_names)
116
+ last = self._engine.choice(self._last_names)
117
+ return f"{first} {last}"
118
+
119
+ firsts = self._engine.choices(self._first_names, count)
120
+ lasts = self._engine.choices(self._last_names, count)
121
+ return [f"{f} {ln}" for f, ln in zip(firsts, lasts)]
122
+
123
+ @overload
124
+ def prefix(self) -> str: ...
125
+ @overload
126
+ def prefix(self, count: Literal[1]) -> str: ...
127
+ @overload
128
+ def prefix(self, count: int) -> str | list[str]: ...
129
+ def prefix(self, count: int = 1) -> str | list[str]:
130
+ """Generate a name prefix (Mr., Mrs., Ms., Dr.).
131
+
132
+ Parameters
133
+ ----------
134
+ count : int
135
+ Number of prefixes to generate.
136
+ """
137
+ prefixes = _PREFIXES
138
+ if count == 1:
139
+ return self._engine.choice(prefixes)
140
+ return self._engine.choices(prefixes, count)
141
+
142
+ @overload
143
+ def suffix(self) -> str: ...
144
+ @overload
145
+ def suffix(self, count: Literal[1]) -> str: ...
146
+ @overload
147
+ def suffix(self, count: int) -> str | list[str]: ...
148
+ def suffix(self, count: int = 1) -> str | list[str]:
149
+ """Generate a name suffix (Jr., Sr., III, IV, V).
150
+
151
+ Parameters
152
+ ----------
153
+ count : int
154
+ Number of suffixes to generate.
155
+ """
156
+ suffixes = _SUFFIXES
157
+ if count == 1:
158
+ return self._engine.choice(suffixes)
159
+ return self._engine.choices(suffixes, count)
160
+
161
+ @overload
162
+ def male_first_name(self) -> str: ...
163
+ @overload
164
+ def male_first_name(self, count: Literal[1]) -> str: ...
165
+ @overload
166
+ def male_first_name(self, count: int) -> str | list[str]: ...
167
+ def male_first_name(self, count: int = 1) -> str | list[str]:
168
+ """Generate a random male first name.
169
+
170
+ Parameters
171
+ ----------
172
+ count : int
173
+ Number of names to generate.
174
+ """
175
+ if count == 1:
176
+ return self._engine.choice(self._male_first_names)
177
+ return self._engine.choices(self._male_first_names, count)
178
+
179
+ @overload
180
+ def female_first_name(self) -> str: ...
181
+ @overload
182
+ def female_first_name(self, count: Literal[1]) -> str: ...
183
+ @overload
184
+ def female_first_name(self, count: int) -> str | list[str]: ...
185
+ def female_first_name(self, count: int = 1) -> str | list[str]:
186
+ """Generate a random female first name.
187
+
188
+ Parameters
189
+ ----------
190
+ count : int
191
+ Number of names to generate.
192
+ """
193
+ if count == 1:
194
+ return self._engine.choice(self._female_first_names)
195
+ return self._engine.choices(self._female_first_names, count)
@@ -0,0 +1,87 @@
1
+ """Phone provider — generates fake phone and cell numbers."""
2
+
3
+ from types import ModuleType
4
+ from typing import Literal, overload
5
+
6
+ from dataforge.backend import RandomEngine
7
+ from dataforge.providers.base import BaseProvider
8
+
9
+
10
+ class PhoneProvider(BaseProvider):
11
+ """Generates fake phone and cell phone numbers.
12
+
13
+ Parameters
14
+ ----------
15
+ engine : RandomEngine
16
+ The shared random engine instance.
17
+ locale_data : ModuleType
18
+ The imported locale module (e.g. ``dataforge.locales.en_US.phone``).
19
+ """
20
+
21
+ __slots__ = ("_phone_formats", "_cell_formats")
22
+
23
+ _provider_name = "phone"
24
+ _locale_modules = ("phone",)
25
+ _field_map = {
26
+ "phone_number": "phone_number",
27
+ "phone": "phone_number",
28
+ "cell_phone": "cell_phone",
29
+ "cell": "cell_phone",
30
+ }
31
+
32
+ def __init__(self, engine: RandomEngine, locale_data: ModuleType) -> None:
33
+ super().__init__(engine)
34
+ self._phone_formats: tuple[str, ...] = locale_data.phone_formats
35
+ self._cell_formats: tuple[str, ...] = locale_data.cell_formats
36
+
37
+ # ------------------------------------------------------------------
38
+ # Scalar helpers
39
+ # ------------------------------------------------------------------
40
+
41
+ def _one_phone(self) -> str:
42
+ fmt = self._engine.choice(self._phone_formats)
43
+ return self._engine.numerify(fmt)
44
+
45
+ def _one_cell(self) -> str:
46
+ fmt = self._engine.choice(self._cell_formats)
47
+ return self._engine.numerify(fmt)
48
+
49
+ # ------------------------------------------------------------------
50
+ # Public API
51
+ # ------------------------------------------------------------------
52
+
53
+ @overload
54
+ def phone_number(self) -> str: ...
55
+ @overload
56
+ def phone_number(self, count: Literal[1]) -> str: ...
57
+ @overload
58
+ def phone_number(self, count: int) -> str | list[str]: ...
59
+ def phone_number(self, count: int = 1) -> str | list[str]:
60
+ """Generate a random phone number.
61
+
62
+ Parameters
63
+ ----------
64
+ count : int
65
+ Number of phone numbers to generate.
66
+ """
67
+ if count == 1:
68
+ return self._one_phone()
69
+ return [self._one_phone() for _ in range(count)]
70
+
71
+ @overload
72
+ def cell_phone(self) -> str: ...
73
+ @overload
74
+ def cell_phone(self, count: Literal[1]) -> str: ...
75
+ @overload
76
+ def cell_phone(self, count: int) -> str | list[str]: ...
77
+ def cell_phone(self, count: int = 1) -> str | list[str]:
78
+ """Generate a random cell phone number.
79
+
80
+ Parameters
81
+ ----------
82
+ count : int
83
+ Number of cell phone numbers to generate.
84
+ """
85
+ if count == 1:
86
+ return self._one_cell()
87
+ return [self._one_cell() for _ in range(count)]