fakeforge-br 0.1.0__tar.gz

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.
@@ -0,0 +1,13 @@
1
+ .venv
2
+ __pycache__
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .pytest_cache
7
+ .mypy_cache
8
+ .ruff_cache
9
+ dist
10
+ build
11
+ *.egg-info
12
+ .DS_Store
13
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Everton Paula
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,345 @@
1
+ Metadata-Version: 2.5
2
+ Name: fakeforge-br
3
+ Version: 0.1.0
4
+ Summary: SDK oficial do FakeForge para gerar dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) em testes de software. Zero dependências runtime.
5
+ Project-URL: Homepage, https://fakeforge.com.br
6
+ Project-URL: Documentation, https://fakeforge.com.br/docs
7
+ Project-URL: Repository, https://github.com/everpaula/fakeforge-br
8
+ Project-URL: Issues, https://github.com/everpaula/fakeforge-br/issues
9
+ Author-email: Everton Paula <hey@fakeforge.com.br>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: brasil,brazil,brazilian,cep,cnpj,cpf,fake-data,faker,fixture,gerador,mock,pix,python,seed,test-data,testing
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: Portuguese (Brazilian)
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Software Development :: Testing
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.8
29
+ Provides-Extra: dev
30
+ Requires-Dist: mypy>=1.0; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.1; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # fakeforge
37
+
38
+ SDK oficial do [FakeForge](https://fakeforge.com.br) para Python — gera dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) para testes de software.
39
+
40
+ - ✅ **Zero dependências runtime** (usa `urllib` nativo)
41
+ - ✅ **Type hints completos** (compatível com mypy strict)
42
+ - ✅ **Python 3.8+**
43
+ - ✅ **Validação real** — todos os documentos passam mod-11 da Receita Federal, Luhn, ANATEL
44
+ - ✅ **Presets correlacionados** — pessoa completa com CPF + email + endereço + telefone em 1 chamada
45
+ - ✅ **CNPJ alfanumérico 2026** — cobertura do novo formato (IN RFB 2.229)
46
+ - ✅ **Grátis** — 50 chamadas/dia sem API key, ou 10.000/dia com plano Dev (R$29/mês)
47
+
48
+ ## Instalação
49
+
50
+ ```bash
51
+ pip install fakeforge-br
52
+ ```
53
+
54
+ ```bash
55
+ poetry add fakeforge
56
+ ```
57
+
58
+ ```bash
59
+ uv add fakeforge
60
+ ```
61
+
62
+ ## Uso rápido
63
+
64
+ ```python
65
+ from fakeforge import FakeForge
66
+
67
+ ff = FakeForge()
68
+
69
+ # CPFs válidos (mod-11 da Receita Federal)
70
+ cpfs = ff.cpf(10)
71
+ # ['123.456.789-09', '987.654.321-00', ...]
72
+
73
+ # CNPJs válidos (mod-11)
74
+ cnpjs = ff.cnpj(5)
75
+
76
+ # Chave PIX no formato BACEN
77
+ pix = ff.pix_key(3)
78
+
79
+ # Cartão de crédito com Luhn válido
80
+ cards = ff.credit_card(5)
81
+ # [{'number': '...', 'brand': 'visa', 'cvv': '123', 'expiry': '12/28'}, ...]
82
+
83
+ # Pessoa completa correlacionada
84
+ [pessoa] = ff.person(1)
85
+ print(pessoa["name"], pessoa["cpf"], pessoa["email"])
86
+ ```
87
+
88
+ ## Presets: dados correlacionados em 1 chamada
89
+
90
+ Presets retornam objetos com múltiplos campos que se relacionam — email deriva do nome, DDD bate com o estado do endereço, etc.
91
+
92
+ ```python
93
+ customers = ff.preset("customer", 100)
94
+
95
+ for c in customers:
96
+ print({
97
+ "name": c["name"],
98
+ "cpf": c["cpf"],
99
+ "email": c["email"],
100
+ "phone": c["phone"],
101
+ "address": c["address"],
102
+ })
103
+ ```
104
+
105
+ Presets disponíveis:
106
+
107
+ | Preset | Retorna |
108
+ |---|---|
109
+ | `customer` | pessoa + endereço + email + telefone + PIX |
110
+ | `employee` | pessoa + conta bancária + PIX |
111
+ | `company` | empresa + endereço + contato |
112
+ | `ecommerce_order` | cliente + cartão + entrega |
113
+ | `contact_list` | nome + email + telefone |
114
+
115
+ ## Comparação com faker (pt-BR) e python-brasilidades
116
+
117
+ | Recurso | faker (pt-BR) | python-brasilidades | fakeforge |
118
+ |---|---|---|---|
119
+ | CPF com mod-11 válido | ❌ | ✅ | ✅ |
120
+ | CNPJ com mod-11 válido | ❌ | ✅ | ✅ |
121
+ | CNPJ alfanumérico 2026 | ❌ | ❌ | ✅ |
122
+ | Cartão com Luhn | ❌ | ❌ | ✅ |
123
+ | PIX BACEN (4 formatos) | ❌ | ❌ | ✅ |
124
+ | Correlação nome ↔ email ↔ DDD | ❌ | ❌ | ✅ |
125
+ | DDDs oficiais ANATEL | ❌ | Parcial | ✅ (67 DDDs) |
126
+ | 17 bancos brasileiros com DV | ❌ | ❌ | ✅ |
127
+ | Presets bundle (customer, employee, etc) | ❌ | ❌ | ✅ |
128
+ | API HTTP (sem instalar dep em outra linguagem) | ❌ | ❌ | ✅ |
129
+
130
+ **fakeforge** é o único com API HTTP + SDK Python que permite escalar geração em CI/CD sem instalar dep de biblioteca em cada linguagem do stack. Perfeito pra times que usam Python no backend mas Node no frontend.
131
+
132
+ ## Uso com pytest
133
+
134
+ ### Fixture reutilizável
135
+
136
+ ```python
137
+ # conftest.py
138
+ import pytest
139
+ from fakeforge import FakeForge
140
+
141
+ @pytest.fixture(scope="session")
142
+ def customers():
143
+ """100 customers correlacionados. Escopo session pra reutilizar entre testes."""
144
+ ff = FakeForge()
145
+ return ff.preset("customer", 100)
146
+
147
+
148
+ @pytest.fixture(scope="session")
149
+ def cpfs_validos():
150
+ """1000 CPFs válidos pra teste de load."""
151
+ ff = FakeForge()
152
+ return ff.cpf(1000)
153
+ ```
154
+
155
+ ```python
156
+ # test_checkout.py
157
+ def test_checkout_aceita_cpf_valido(customers, client):
158
+ for customer in customers[:20]:
159
+ response = client.post("/checkout", json={
160
+ "cpf": customer["cpf"],
161
+ "email": customer["email"],
162
+ })
163
+ assert response.status_code == 200
164
+ ```
165
+
166
+ ### Django ORM seed
167
+
168
+ ```python
169
+ # management/commands/seed_customers.py
170
+ from django.core.management.base import BaseCommand
171
+ from fakeforge import FakeForge
172
+ from myapp.models import Customer
173
+
174
+ class Command(BaseCommand):
175
+ help = "Popula banco com 1000 customers via FakeForge"
176
+
177
+ def handle(self, *args, **options):
178
+ ff = FakeForge(api_key="sua_key_dev") # 10.000/dia no Dev
179
+ customers = ff.preset("customer", 1000)
180
+
181
+ Customer.objects.bulk_create([
182
+ Customer(
183
+ cpf=c["cpf"],
184
+ name=c["name"],
185
+ email=c["email"],
186
+ phone=c["phone"],
187
+ )
188
+ for c in customers
189
+ ])
190
+
191
+ self.stdout.write(f"✓ {len(customers)} customers inseridos")
192
+ ```
193
+
194
+ ### FastAPI mock
195
+
196
+ ```python
197
+ # tests/conftest.py
198
+ import pytest
199
+ from httpx import AsyncClient
200
+ from fakeforge import FakeForge
201
+
202
+ @pytest.fixture
203
+ def fake_customer():
204
+ ff = FakeForge()
205
+ return ff.preset("customer", 1)[0]
206
+
207
+ @pytest.mark.asyncio
208
+ async def test_signup(fake_customer, client: AsyncClient):
209
+ response = await client.post("/signup", json=fake_customer)
210
+ assert response.status_code == 201
211
+ ```
212
+
213
+ ## API key opcional (plano Dev/Team)
214
+
215
+ Sem API key: 50 chamadas/dia por IP, até 100 items por chamada. Perfeito pra dev local.
216
+
217
+ Com API key do plano [Dev (R$29/mês)](https://fakeforge.com.br/pricing?plan=dev): 10.000 chamadas/dia, até 10.000 items por chamada. Ideal pra CI/CD, seed em produção, load test.
218
+
219
+ ```python
220
+ import os
221
+ from fakeforge import FakeForge
222
+
223
+ ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
224
+ cpfs = ff.cpf(10_000) # no Dev, cabe em 1 chamada
225
+ ```
226
+
227
+ Pegue sua API key em [fakeforge.com.br/dashboard](https://fakeforge.com.br/dashboard).
228
+
229
+ ## Tratamento de erros
230
+
231
+ ```python
232
+ from fakeforge import FakeForge, FakeForgeError
233
+
234
+ ff = FakeForge()
235
+
236
+ try:
237
+ cpfs = ff.cpf(1000)
238
+ except FakeForgeError as e:
239
+ if e.status == 429:
240
+ print(f"Rate limit: {e.used_today}/{e.daily_limit}")
241
+ print(f"Upgrade: {e.upgrade_url}")
242
+ else:
243
+ raise
244
+ ```
245
+
246
+ ## API completa
247
+
248
+ ### Documentos pessoais
249
+
250
+ - `cpf(quantity=1, formatted=True) -> list[str]`
251
+ - `cnpj(quantity=1, formatted=True) -> list[str]`
252
+ - `cnpj_alfa(quantity=1, formatted=True) -> list[str]` — novo formato 2026
253
+ - `cnh(quantity=1, formatted=True) -> list[str]`
254
+ - `rg(quantity=1, formatted=True) -> list[str]`
255
+ - `pis(quantity=1, formatted=True) -> list[str]`
256
+ - `renavam(quantity=1, formatted=True) -> list[str]`
257
+ - `titulo_eleitor(quantity=1, formatted=True) -> list[str]`
258
+ - `placa(quantity=1) -> list[str]`
259
+
260
+ ### Contato
261
+
262
+ - `email(quantity=1) -> list[str]`
263
+ - `phone(quantity=1, formatted=True) -> list[str]` — celular ANATEL
264
+ - `landline(quantity=1, formatted=True) -> list[str]` — fixo
265
+
266
+ ### Endereço
267
+
268
+ - `cep(quantity=1, formatted=True) -> list[str]`
269
+ - `address(quantity=1, formatted=True) -> list[dict]`
270
+
271
+ ### Pessoa completa
272
+
273
+ - `person(quantity=1, formatted=True) -> list[dict]`
274
+ - `full_name(quantity=1) -> list[str]`
275
+
276
+ ### Financeiro
277
+
278
+ - `credit_card(quantity=1, formatted=True) -> list[dict]`
279
+ - `pix_key(quantity=1) -> list[str]`
280
+ - `bank_account(quantity=1, formatted=True) -> list[dict]`
281
+
282
+ ### Empresa
283
+
284
+ - `company(quantity=1, formatted=True) -> list[dict]`
285
+
286
+ ### Presets
287
+
288
+ - `preset(name, quantity=1, formatted=True) -> list[dict]`
289
+
290
+ ### Genérico
291
+
292
+ - `generate(type_, quantity=1, formatted=True) -> list[Any]`
293
+
294
+ ## Perguntas frequentes
295
+
296
+ ### É legal usar CPFs/CNPJs gerados em testes?
297
+
298
+ Sim. Gerar números que passam validação matemática (mod-11) pra fins de teste é prática padrão em desenvolvimento. Crime é usar CPF/CNPJ (fake ou real) pra fraude, sonegação ou cadastro em nome de terceiro.
299
+
300
+ ### Os dados batem no DICT/SPC/Serasa?
301
+
302
+ Não. São dados matematicamente válidos mas não existem em nenhuma base oficial. Perfeito pra teste de formato, validação de schema e seed de staging. Não serve pra teste com API externa que consulta base real.
303
+
304
+ ### Como configurar em CI (GitHub Actions)?
305
+
306
+ ```yaml
307
+ - name: Rodar testes com FakeForge
308
+ env:
309
+ FAKEFORGE_API_KEY: ${{ secrets.FAKEFORGE_API_KEY }}
310
+ run: pytest
311
+ ```
312
+
313
+ Cache dos dados na primeira chamada evita esgotar quota:
314
+
315
+ ```python
316
+ # tests/fixtures.py
317
+ import json
318
+ from pathlib import Path
319
+ from fakeforge import FakeForge
320
+
321
+ CACHE = Path(__file__).parent / "customers.json"
322
+
323
+ def get_customers():
324
+ if CACHE.exists():
325
+ return json.loads(CACHE.read_text())
326
+
327
+ ff = FakeForge()
328
+ customers = ff.preset("customer", 100)
329
+ CACHE.write_text(json.dumps(customers, indent=2, ensure_ascii=False))
330
+ return customers
331
+ ```
332
+
333
+ ## Suporte
334
+
335
+ - 📚 Docs completos: [fakeforge.com.br/docs](https://fakeforge.com.br/docs)
336
+ - 💬 Email direto: `hey@fakeforge.com.br`
337
+ - 🐛 Issues: [github.com/everpaula/fakeforge-br/issues](https://github.com/everpaula/fakeforge-br/issues)
338
+
339
+ ## Licença
340
+
341
+ MIT — veja [LICENSE](./LICENSE) para detalhes.
342
+
343
+ ---
344
+
345
+ Feito por [Everton Paula](https://fakeforge.com.br) — engenheiro brasileiro que precisou de dados válidos pra testar checkout PIX e escreveu essa lib porque nenhuma outra funcionava direito.
@@ -0,0 +1,310 @@
1
+ # fakeforge
2
+
3
+ SDK oficial do [FakeForge](https://fakeforge.com.br) para Python — gera dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) para testes de software.
4
+
5
+ - ✅ **Zero dependências runtime** (usa `urllib` nativo)
6
+ - ✅ **Type hints completos** (compatível com mypy strict)
7
+ - ✅ **Python 3.8+**
8
+ - ✅ **Validação real** — todos os documentos passam mod-11 da Receita Federal, Luhn, ANATEL
9
+ - ✅ **Presets correlacionados** — pessoa completa com CPF + email + endereço + telefone em 1 chamada
10
+ - ✅ **CNPJ alfanumérico 2026** — cobertura do novo formato (IN RFB 2.229)
11
+ - ✅ **Grátis** — 50 chamadas/dia sem API key, ou 10.000/dia com plano Dev (R$29/mês)
12
+
13
+ ## Instalação
14
+
15
+ ```bash
16
+ pip install fakeforge-br
17
+ ```
18
+
19
+ ```bash
20
+ poetry add fakeforge
21
+ ```
22
+
23
+ ```bash
24
+ uv add fakeforge
25
+ ```
26
+
27
+ ## Uso rápido
28
+
29
+ ```python
30
+ from fakeforge import FakeForge
31
+
32
+ ff = FakeForge()
33
+
34
+ # CPFs válidos (mod-11 da Receita Federal)
35
+ cpfs = ff.cpf(10)
36
+ # ['123.456.789-09', '987.654.321-00', ...]
37
+
38
+ # CNPJs válidos (mod-11)
39
+ cnpjs = ff.cnpj(5)
40
+
41
+ # Chave PIX no formato BACEN
42
+ pix = ff.pix_key(3)
43
+
44
+ # Cartão de crédito com Luhn válido
45
+ cards = ff.credit_card(5)
46
+ # [{'number': '...', 'brand': 'visa', 'cvv': '123', 'expiry': '12/28'}, ...]
47
+
48
+ # Pessoa completa correlacionada
49
+ [pessoa] = ff.person(1)
50
+ print(pessoa["name"], pessoa["cpf"], pessoa["email"])
51
+ ```
52
+
53
+ ## Presets: dados correlacionados em 1 chamada
54
+
55
+ Presets retornam objetos com múltiplos campos que se relacionam — email deriva do nome, DDD bate com o estado do endereço, etc.
56
+
57
+ ```python
58
+ customers = ff.preset("customer", 100)
59
+
60
+ for c in customers:
61
+ print({
62
+ "name": c["name"],
63
+ "cpf": c["cpf"],
64
+ "email": c["email"],
65
+ "phone": c["phone"],
66
+ "address": c["address"],
67
+ })
68
+ ```
69
+
70
+ Presets disponíveis:
71
+
72
+ | Preset | Retorna |
73
+ |---|---|
74
+ | `customer` | pessoa + endereço + email + telefone + PIX |
75
+ | `employee` | pessoa + conta bancária + PIX |
76
+ | `company` | empresa + endereço + contato |
77
+ | `ecommerce_order` | cliente + cartão + entrega |
78
+ | `contact_list` | nome + email + telefone |
79
+
80
+ ## Comparação com faker (pt-BR) e python-brasilidades
81
+
82
+ | Recurso | faker (pt-BR) | python-brasilidades | fakeforge |
83
+ |---|---|---|---|
84
+ | CPF com mod-11 válido | ❌ | ✅ | ✅ |
85
+ | CNPJ com mod-11 válido | ❌ | ✅ | ✅ |
86
+ | CNPJ alfanumérico 2026 | ❌ | ❌ | ✅ |
87
+ | Cartão com Luhn | ❌ | ❌ | ✅ |
88
+ | PIX BACEN (4 formatos) | ❌ | ❌ | ✅ |
89
+ | Correlação nome ↔ email ↔ DDD | ❌ | ❌ | ✅ |
90
+ | DDDs oficiais ANATEL | ❌ | Parcial | ✅ (67 DDDs) |
91
+ | 17 bancos brasileiros com DV | ❌ | ❌ | ✅ |
92
+ | Presets bundle (customer, employee, etc) | ❌ | ❌ | ✅ |
93
+ | API HTTP (sem instalar dep em outra linguagem) | ❌ | ❌ | ✅ |
94
+
95
+ **fakeforge** é o único com API HTTP + SDK Python que permite escalar geração em CI/CD sem instalar dep de biblioteca em cada linguagem do stack. Perfeito pra times que usam Python no backend mas Node no frontend.
96
+
97
+ ## Uso com pytest
98
+
99
+ ### Fixture reutilizável
100
+
101
+ ```python
102
+ # conftest.py
103
+ import pytest
104
+ from fakeforge import FakeForge
105
+
106
+ @pytest.fixture(scope="session")
107
+ def customers():
108
+ """100 customers correlacionados. Escopo session pra reutilizar entre testes."""
109
+ ff = FakeForge()
110
+ return ff.preset("customer", 100)
111
+
112
+
113
+ @pytest.fixture(scope="session")
114
+ def cpfs_validos():
115
+ """1000 CPFs válidos pra teste de load."""
116
+ ff = FakeForge()
117
+ return ff.cpf(1000)
118
+ ```
119
+
120
+ ```python
121
+ # test_checkout.py
122
+ def test_checkout_aceita_cpf_valido(customers, client):
123
+ for customer in customers[:20]:
124
+ response = client.post("/checkout", json={
125
+ "cpf": customer["cpf"],
126
+ "email": customer["email"],
127
+ })
128
+ assert response.status_code == 200
129
+ ```
130
+
131
+ ### Django ORM seed
132
+
133
+ ```python
134
+ # management/commands/seed_customers.py
135
+ from django.core.management.base import BaseCommand
136
+ from fakeforge import FakeForge
137
+ from myapp.models import Customer
138
+
139
+ class Command(BaseCommand):
140
+ help = "Popula banco com 1000 customers via FakeForge"
141
+
142
+ def handle(self, *args, **options):
143
+ ff = FakeForge(api_key="sua_key_dev") # 10.000/dia no Dev
144
+ customers = ff.preset("customer", 1000)
145
+
146
+ Customer.objects.bulk_create([
147
+ Customer(
148
+ cpf=c["cpf"],
149
+ name=c["name"],
150
+ email=c["email"],
151
+ phone=c["phone"],
152
+ )
153
+ for c in customers
154
+ ])
155
+
156
+ self.stdout.write(f"✓ {len(customers)} customers inseridos")
157
+ ```
158
+
159
+ ### FastAPI mock
160
+
161
+ ```python
162
+ # tests/conftest.py
163
+ import pytest
164
+ from httpx import AsyncClient
165
+ from fakeforge import FakeForge
166
+
167
+ @pytest.fixture
168
+ def fake_customer():
169
+ ff = FakeForge()
170
+ return ff.preset("customer", 1)[0]
171
+
172
+ @pytest.mark.asyncio
173
+ async def test_signup(fake_customer, client: AsyncClient):
174
+ response = await client.post("/signup", json=fake_customer)
175
+ assert response.status_code == 201
176
+ ```
177
+
178
+ ## API key opcional (plano Dev/Team)
179
+
180
+ Sem API key: 50 chamadas/dia por IP, até 100 items por chamada. Perfeito pra dev local.
181
+
182
+ Com API key do plano [Dev (R$29/mês)](https://fakeforge.com.br/pricing?plan=dev): 10.000 chamadas/dia, até 10.000 items por chamada. Ideal pra CI/CD, seed em produção, load test.
183
+
184
+ ```python
185
+ import os
186
+ from fakeforge import FakeForge
187
+
188
+ ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
189
+ cpfs = ff.cpf(10_000) # no Dev, cabe em 1 chamada
190
+ ```
191
+
192
+ Pegue sua API key em [fakeforge.com.br/dashboard](https://fakeforge.com.br/dashboard).
193
+
194
+ ## Tratamento de erros
195
+
196
+ ```python
197
+ from fakeforge import FakeForge, FakeForgeError
198
+
199
+ ff = FakeForge()
200
+
201
+ try:
202
+ cpfs = ff.cpf(1000)
203
+ except FakeForgeError as e:
204
+ if e.status == 429:
205
+ print(f"Rate limit: {e.used_today}/{e.daily_limit}")
206
+ print(f"Upgrade: {e.upgrade_url}")
207
+ else:
208
+ raise
209
+ ```
210
+
211
+ ## API completa
212
+
213
+ ### Documentos pessoais
214
+
215
+ - `cpf(quantity=1, formatted=True) -> list[str]`
216
+ - `cnpj(quantity=1, formatted=True) -> list[str]`
217
+ - `cnpj_alfa(quantity=1, formatted=True) -> list[str]` — novo formato 2026
218
+ - `cnh(quantity=1, formatted=True) -> list[str]`
219
+ - `rg(quantity=1, formatted=True) -> list[str]`
220
+ - `pis(quantity=1, formatted=True) -> list[str]`
221
+ - `renavam(quantity=1, formatted=True) -> list[str]`
222
+ - `titulo_eleitor(quantity=1, formatted=True) -> list[str]`
223
+ - `placa(quantity=1) -> list[str]`
224
+
225
+ ### Contato
226
+
227
+ - `email(quantity=1) -> list[str]`
228
+ - `phone(quantity=1, formatted=True) -> list[str]` — celular ANATEL
229
+ - `landline(quantity=1, formatted=True) -> list[str]` — fixo
230
+
231
+ ### Endereço
232
+
233
+ - `cep(quantity=1, formatted=True) -> list[str]`
234
+ - `address(quantity=1, formatted=True) -> list[dict]`
235
+
236
+ ### Pessoa completa
237
+
238
+ - `person(quantity=1, formatted=True) -> list[dict]`
239
+ - `full_name(quantity=1) -> list[str]`
240
+
241
+ ### Financeiro
242
+
243
+ - `credit_card(quantity=1, formatted=True) -> list[dict]`
244
+ - `pix_key(quantity=1) -> list[str]`
245
+ - `bank_account(quantity=1, formatted=True) -> list[dict]`
246
+
247
+ ### Empresa
248
+
249
+ - `company(quantity=1, formatted=True) -> list[dict]`
250
+
251
+ ### Presets
252
+
253
+ - `preset(name, quantity=1, formatted=True) -> list[dict]`
254
+
255
+ ### Genérico
256
+
257
+ - `generate(type_, quantity=1, formatted=True) -> list[Any]`
258
+
259
+ ## Perguntas frequentes
260
+
261
+ ### É legal usar CPFs/CNPJs gerados em testes?
262
+
263
+ Sim. Gerar números que passam validação matemática (mod-11) pra fins de teste é prática padrão em desenvolvimento. Crime é usar CPF/CNPJ (fake ou real) pra fraude, sonegação ou cadastro em nome de terceiro.
264
+
265
+ ### Os dados batem no DICT/SPC/Serasa?
266
+
267
+ Não. São dados matematicamente válidos mas não existem em nenhuma base oficial. Perfeito pra teste de formato, validação de schema e seed de staging. Não serve pra teste com API externa que consulta base real.
268
+
269
+ ### Como configurar em CI (GitHub Actions)?
270
+
271
+ ```yaml
272
+ - name: Rodar testes com FakeForge
273
+ env:
274
+ FAKEFORGE_API_KEY: ${{ secrets.FAKEFORGE_API_KEY }}
275
+ run: pytest
276
+ ```
277
+
278
+ Cache dos dados na primeira chamada evita esgotar quota:
279
+
280
+ ```python
281
+ # tests/fixtures.py
282
+ import json
283
+ from pathlib import Path
284
+ from fakeforge import FakeForge
285
+
286
+ CACHE = Path(__file__).parent / "customers.json"
287
+
288
+ def get_customers():
289
+ if CACHE.exists():
290
+ return json.loads(CACHE.read_text())
291
+
292
+ ff = FakeForge()
293
+ customers = ff.preset("customer", 100)
294
+ CACHE.write_text(json.dumps(customers, indent=2, ensure_ascii=False))
295
+ return customers
296
+ ```
297
+
298
+ ## Suporte
299
+
300
+ - 📚 Docs completos: [fakeforge.com.br/docs](https://fakeforge.com.br/docs)
301
+ - 💬 Email direto: `hey@fakeforge.com.br`
302
+ - 🐛 Issues: [github.com/everpaula/fakeforge-br/issues](https://github.com/everpaula/fakeforge-br/issues)
303
+
304
+ ## Licença
305
+
306
+ MIT — veja [LICENSE](./LICENSE) para detalhes.
307
+
308
+ ---
309
+
310
+ Feito por [Everton Paula](https://fakeforge.com.br) — engenheiro brasileiro que precisou de dados válidos pra testar checkout PIX e escreveu essa lib porque nenhuma outra funcionava direito.
@@ -0,0 +1,68 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "fakeforge-br"
7
+ version = "0.1.0"
8
+ description = "SDK oficial do FakeForge para gerar dados brasileiros válidos (CPF, CNPJ, CEP, PIX, cartão de crédito) em testes de software. Zero dependências runtime."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Everton Paula", email = "hey@fakeforge.com.br" }
14
+ ]
15
+ keywords = [
16
+ "cpf", "cnpj", "cep", "pix", "gerador", "brazilian", "brazil",
17
+ "faker", "testing", "mock", "fixture", "seed", "fake-data",
18
+ "test-data", "python", "brasil"
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Natural Language :: Portuguese (Brazilian)",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3 :: Only",
28
+ "Programming Language :: Python :: 3.8",
29
+ "Programming Language :: Python :: 3.9",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ "Topic :: Software Development :: Libraries :: Python Modules",
34
+ "Topic :: Software Development :: Testing",
35
+ "Typing :: Typed",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://fakeforge.com.br"
40
+ Documentation = "https://fakeforge.com.br/docs"
41
+ Repository = "https://github.com/everpaula/fakeforge-br"
42
+ Issues = "https://github.com/everpaula/fakeforge-br/issues"
43
+
44
+ [project.optional-dependencies]
45
+ dev = [
46
+ "pytest>=7.0",
47
+ "pytest-asyncio>=0.21",
48
+ "mypy>=1.0",
49
+ "ruff>=0.1",
50
+ ]
51
+
52
+ [tool.hatch.build.targets.wheel]
53
+ packages = ["src/fakeforge"]
54
+
55
+ [tool.hatch.build.targets.sdist]
56
+ include = [
57
+ "src/fakeforge",
58
+ "README.md",
59
+ "LICENSE",
60
+ ]
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py38"
65
+
66
+ [tool.mypy]
67
+ python_version = "3.8"
68
+ strict = true
@@ -0,0 +1,28 @@
1
+ """
2
+ fakeforge - SDK oficial pra gerar dados brasileiros válidos.
3
+
4
+ Gere CPFs, CNPJs, CEPs, chaves PIX e cartões de crédito com validação
5
+ real (mod-11, Luhn, ANATEL) para testes de software, seed de banco e
6
+ fixture de QA.
7
+
8
+ Uso rápido:
9
+
10
+ from fakeforge import FakeForge
11
+
12
+ ff = FakeForge()
13
+ cpfs = ff.cpf(10) # 10 CPFs válidos
14
+ customers = ff.preset("customer", 100) # 100 pessoas correlacionadas
15
+
16
+ Para volumes maiores, use API key do plano Dev:
17
+
18
+ ff = FakeForge(api_key="sua_key")
19
+ cpfs = ff.cpf(10000) # até 10.000 por chamada
20
+
21
+ Homepage: https://fakeforge.com.br
22
+ """
23
+
24
+ from fakeforge.client import FakeForge
25
+ from fakeforge.exceptions import FakeForgeError
26
+
27
+ __version__ = "0.1.0"
28
+ __all__ = ["FakeForge", "FakeForgeError"]
@@ -0,0 +1,218 @@
1
+ """Cliente principal do SDK FakeForge."""
2
+
3
+ import json
4
+ import urllib.error
5
+ import urllib.parse
6
+ import urllib.request
7
+ from typing import Any, Optional, TypeVar, cast
8
+
9
+ from fakeforge.exceptions import FakeForgeError
10
+
11
+ T = TypeVar("T")
12
+
13
+ DEFAULT_BASE_URL = "https://fakeforge.com.br"
14
+ DEFAULT_TIMEOUT = 30.0
15
+ USER_AGENT = "fakeforge-python-sdk/0.1.0"
16
+
17
+
18
+ class FakeForge:
19
+ """
20
+ Cliente principal do SDK FakeForge.
21
+
22
+ Example:
23
+ >>> from fakeforge import FakeForge
24
+ >>> ff = FakeForge()
25
+ >>> cpfs = ff.cpf(10) # 10 CPFs válidos (mod-11)
26
+ >>> customers = ff.preset("customer", 100) # pessoa correlacionada
27
+
28
+ Example com API key (plano Dev/Team):
29
+ >>> import os
30
+ >>> ff = FakeForge(api_key=os.environ["FAKEFORGE_API_KEY"])
31
+ >>> cpfs = ff.cpf(10000) # até 10.000 por chamada no Dev
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ api_key: Optional[str] = None,
37
+ base_url: str = DEFAULT_BASE_URL,
38
+ timeout: float = DEFAULT_TIMEOUT,
39
+ ) -> None:
40
+ """
41
+ Args:
42
+ api_key: API key opcional (plano Dev/Team). Sem key, usa tier
43
+ grátis: 50 chamadas/dia por IP.
44
+ base_url: URL base da API. Só mude se for self-hosted.
45
+ timeout: Timeout em segundos pra cada request. Default: 30s.
46
+ """
47
+ self.api_key = api_key
48
+ self.base_url = base_url.rstrip("/")
49
+ self.timeout = timeout
50
+
51
+ def _request(
52
+ self,
53
+ type_or_preset: str,
54
+ quantity: int = 1,
55
+ formatted: bool = True,
56
+ is_preset: bool = False,
57
+ ) -> list[Any]:
58
+ params = {
59
+ "quantity": str(quantity),
60
+ "formatted": "true" if formatted else "false",
61
+ }
62
+ if is_preset:
63
+ params["preset"] = type_or_preset
64
+ else:
65
+ params["type"] = type_or_preset
66
+
67
+ url = f"{self.base_url}/api/generate?{urllib.parse.urlencode(params)}"
68
+
69
+ headers = {
70
+ "Content-Type": "application/json",
71
+ "User-Agent": USER_AGENT,
72
+ }
73
+ if self.api_key:
74
+ headers["X-API-Key"] = self.api_key
75
+
76
+ req = urllib.request.Request(url, headers=headers, method="GET")
77
+
78
+ try:
79
+ with urllib.request.urlopen(req, timeout=self.timeout) as response:
80
+ status = response.status
81
+ body_bytes = response.read()
82
+ except urllib.error.HTTPError as e:
83
+ status = e.code
84
+ body_bytes = e.read()
85
+ except urllib.error.URLError as e:
86
+ raise FakeForgeError(f"Erro de rede: {e.reason}", status=0)
87
+ except TimeoutError:
88
+ raise FakeForgeError(f"Timeout após {self.timeout}s", status=0)
89
+
90
+ try:
91
+ body = json.loads(body_bytes)
92
+ except json.JSONDecodeError:
93
+ raise FakeForgeError(f"Resposta inválida ({status})", status=status)
94
+
95
+ if status >= 400:
96
+ message = body.get("message") or body.get("error") or f"HTTP {status}"
97
+ raise FakeForgeError(message, status=status, body=body)
98
+
99
+ data = body.get("data", [])
100
+ return cast(list[Any], data)
101
+
102
+ # === Documentos pessoais ===
103
+
104
+ def cpf(self, quantity: int = 1, formatted: bool = True) -> list[str]:
105
+ """Gera CPFs válidos (mod-11 da Receita Federal)."""
106
+ return self._request("cpf", quantity, formatted)
107
+
108
+ def cnpj(self, quantity: int = 1, formatted: bool = True) -> list[str]:
109
+ """Gera CNPJs numéricos válidos (mod-11)."""
110
+ return self._request("cnpj", quantity, formatted)
111
+
112
+ def cnpj_alfa(self, quantity: int = 1, formatted: bool = True) -> list[str]:
113
+ """Gera CNPJs no novo formato alfanumérico (IN RFB 2.229, vigência 01/07/2026)."""
114
+ return self._request("cnpjAlfa", quantity, formatted)
115
+
116
+ def cnh(self, quantity: int = 1, formatted: bool = True) -> list[str]:
117
+ """Gera CNHs válidas (mod-11 do DENATRAN)."""
118
+ return self._request("cnh", quantity, formatted)
119
+
120
+ def rg(self, quantity: int = 1, formatted: bool = True) -> list[str]:
121
+ """Gera RGs (formato por estado, mod-11)."""
122
+ return self._request("rg", quantity, formatted)
123
+
124
+ def pis(self, quantity: int = 1, formatted: bool = True) -> list[str]:
125
+ """Gera PIS/PASEP/NIT/NIS válidos."""
126
+ return self._request("pis", quantity, formatted)
127
+
128
+ def renavam(self, quantity: int = 1, formatted: bool = True) -> list[str]:
129
+ """Gera RENAVAMs válidos (mod-11 do DENATRAN)."""
130
+ return self._request("renavam", quantity, formatted)
131
+
132
+ def titulo_eleitor(self, quantity: int = 1, formatted: bool = True) -> list[str]:
133
+ """Gera Títulos de Eleitor válidos."""
134
+ return self._request("tituloEleitor", quantity, formatted)
135
+
136
+ def placa(self, quantity: int = 1) -> list[str]:
137
+ """Gera placas Mercosul (formato LLLNLNN)."""
138
+ return self._request("placa", quantity)
139
+
140
+ # === Contato ===
141
+
142
+ def email(self, quantity: int = 1) -> list[str]:
143
+ """Gera emails com nomes brasileiros e domínios populares."""
144
+ return self._request("email", quantity)
145
+
146
+ def phone(self, quantity: int = 1, formatted: bool = True) -> list[str]:
147
+ """Gera celulares ANATEL (11 dígitos com 9 na frente + DDD válido)."""
148
+ return self._request("phone", quantity, formatted)
149
+
150
+ def landline(self, quantity: int = 1, formatted: bool = True) -> list[str]:
151
+ """Gera telefones fixos residenciais (10 dígitos, sem 9)."""
152
+ return self._request("landline", quantity, formatted)
153
+
154
+ # === Endereço ===
155
+
156
+ def cep(self, quantity: int = 1, formatted: bool = True) -> list[str]:
157
+ """Gera CEPs válidos por estado."""
158
+ return self._request("cep", quantity, formatted)
159
+
160
+ def address(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
161
+ """Gera endereços completos (rua, bairro, cidade, estado, CEP)."""
162
+ return self._request("address", quantity, formatted)
163
+
164
+ # === Pessoa completa ===
165
+
166
+ def person(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
167
+ """Gera pessoa completa correlacionada (nome + CPF + email + telefone)."""
168
+ return self._request("person", quantity, formatted)
169
+
170
+ def full_name(self, quantity: int = 1) -> list[str]:
171
+ """Gera nomes completos brasileiros."""
172
+ return self._request("fullName", quantity)
173
+
174
+ # === Financeiro ===
175
+
176
+ def credit_card(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
177
+ """Gera cartões de crédito com Luhn válido (qualquer bandeira)."""
178
+ return self._request("creditCard", quantity, formatted)
179
+
180
+ def pix_key(self, quantity: int = 1) -> list[str]:
181
+ """Gera chaves PIX no formato BACEN (CPF, email, telefone ou EVP UUID)."""
182
+ return self._request("pixKey", quantity)
183
+
184
+ def bank_account(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
185
+ """Gera conta bancária brasileira (banco + agência + conta com DV)."""
186
+ return self._request("bankAccount", quantity, formatted)
187
+
188
+ # === Empresa ===
189
+
190
+ def company(self, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
191
+ """Gera empresa completa (CNPJ + razão social + endereço)."""
192
+ return self._request("company", quantity, formatted)
193
+
194
+ # === Presets correlacionados ===
195
+
196
+ def preset(self, name: str, quantity: int = 1, formatted: bool = True) -> list[dict[str, Any]]:
197
+ """
198
+ Gera dados correlacionados via preset.
199
+
200
+ Presets disponíveis:
201
+ - customer: pessoa + endereço + email + telefone + PIX
202
+ - employee: pessoa + conta bancária + PIX
203
+ - company: empresa + endereço + contato
204
+ - ecommerce_order: cliente + cartão + entrega
205
+ - contact_list: nome + email + telefone
206
+
207
+ Example:
208
+ >>> customers = ff.preset("customer", 100)
209
+ >>> for c in customers:
210
+ ... print(c["name"], c["cpf"], c["email"])
211
+ """
212
+ return self._request(name, quantity, formatted, is_preset=True)
213
+
214
+ # === Genérico ===
215
+
216
+ def generate(self, type_: str, quantity: int = 1, formatted: bool = True) -> list[Any]:
217
+ """Genérico: chama a API com qualquer type. Útil pra types sem método dedicado."""
218
+ return self._request(type_, quantity, formatted)
@@ -0,0 +1,36 @@
1
+ """Exceções específicas do FakeForge SDK."""
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class FakeForgeError(Exception):
7
+ """
8
+ Erro específico do FakeForge com contexto adicional.
9
+
10
+ Attributes:
11
+ status: Código HTTP retornado pela API (0 se erro de rede).
12
+ code: Nome do erro (rate_limit_exceeded, quantity_limit_exceeded, etc).
13
+ upgrade_url: URL do checkout do plano superior, quando aplicável.
14
+ plan: Plano do user (anon, free, dev, team).
15
+ daily_limit: Limite diário do plano atual.
16
+ used_today: Quantas chamadas já foram feitas hoje.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ message: str,
22
+ status: int = 0,
23
+ body: Optional[dict[str, Any]] = None,
24
+ ) -> None:
25
+ super().__init__(message)
26
+ self.status = status
27
+ body = body or {}
28
+ self.code = str(body.get("error", "unknown"))
29
+ upgrade_obj = body.get("upgrade")
30
+ if isinstance(upgrade_obj, dict):
31
+ self.upgrade_url = upgrade_obj.get("url")
32
+ else:
33
+ self.upgrade_url = body.get("upgrade_url")
34
+ self.plan = body.get("plan")
35
+ self.daily_limit = body.get("daily_limit")
36
+ self.used_today = body.get("your_usage_today")
File without changes