Generate contextually coherent mock data — people, companies, products, and transactions that actually make sense together. 11 locales, 25+ fields per person, realistic distributions, zero dependencies.
Project description
fabrikate 🏭
Contextually coherent mock data generation for Python.
Generate fake people, companies, products, and transactions where everything makes sense together. A person in Tokyo gets a Japanese name, Japanese phone number, Japanese bank, Aadhaar-style ID, a Toyota, and pays via PayPay. A German company has a GmbH suffix, a VAT number, and pays salaries in EUR.
Zero dependencies. Pure Python. 11 locales. 300+ names per locale. 25+ fields per person.
pip install fabrikate
Quick Start
from fabrikate import World
# Create a world set in India
world = World(locale="hi_IN", seed=42, industry="tech")
# Generate a person — everything is Indian and coherent
person = world.person()
print(person.full_name) # "Pooja Reddy"
print(person.phone) # "+91 63794-02654"
print(person.phone_carrier) # "Airtel"
print(person.national_id) # "9600 1338 9083" (Aadhaar format)
print(person.salary) # 1445867.41 (INR)
print(person.credit_card_type) # "RuPay"
print(person.vehicle.brand) # "Maruti Suzuki"
print(person.medical.blood_type) # "O+"
print(person.education.university) # "IIT Delhi"
# Generate a company with employees
company = world.company()
employees = company.hire(10)
# Generate linked transactions
tx = world.transaction(sender=person, receiver=company)
print(tx.payment_method) # "UPI"
print(tx.tax_amount) # 18% GST applied
# Export everything as JSON
print(world.to_json())
Why fabrikate?
Faker generates random data — a person might have an American name, a Japanese phone number, and a German address. fabrikate keeps everything coherent within a cultural and business context.
| Feature | Faker | fabrikate |
|---|---|---|
| Random names | ✓ | ✓ |
| Locale-aware formatting | ✓ | ✓ |
| Coherent person (name + phone + bank + address) | ✗ | ✓ |
| Locale-specific payment methods (UPI, Pix, Zelle) | ✗ | ✓ |
| Locale-weighted blood types, car brands, carriers | ✗ | ✓ |
| National ID formats (Aadhaar, SSN, NI Number) | ✗ | ✓ |
| Company → employee context inheritance | ✗ | ✓ |
| Industry-appropriate job titles and salaries | ✗ | ✓ |
| Realistic distributions (log-normal, bell curve) | ✗ | ✓ |
| Linked transactions with tax and invoices | ✗ | ✓ |
| Full relational dataset export | ✗ | ✓ |
| Zero dependencies | ✗ | ✓ |
Supported Locales
| Code | Country | Currency | Phone | National ID | Top Payment |
|---|---|---|---|---|---|
en_US |
United States | USD ($) | +1 | SSN | Credit Card |
ja_JP |
Japan | JPY (¥) | +81 | My Number | Credit Card |
de_DE |
Germany | EUR (€) | +49 | Personalausweis | Bank Transfer |
pt_BR |
Brazil | BRL (R$) | +55 | CPF | Pix |
hi_IN |
India | INR (₹) | +91 | Aadhaar | UPI |
en_GB |
United Kingdom | GBP (£) | +44 | NI Number | Debit Card |
fr_FR |
France | EUR (€) | +33 | INSEE | Credit Card |
ko_KR |
South Korea | KRW (₩) | +82 | RRN | Credit Card |
es_ES |
Spain | EUR (€) | +34 | DNI | Credit Card |
ar_SA |
Saudi Arabia | SAR (﷼) | +966 | National ID | Mada Card |
en_NG |
Nigeria | NGN (₦) | +234 | NIN | Bank Transfer |
Each locale has ~100 male names, ~100 female names, ~100 last names, 25-35 cities, locale-specific banks, email domains, company suffixes, street formats, and more.
Core Concepts
World
A World is your mock data universe. Everything generated within a world shares context and can reference each other.
world = World(
locale="de_DE", # Country/culture context
seed=123, # Random seed for reproducibility (optional)
year=2025, # Reference year for dates and ages
industry="finance", # Default industry for companies/products
)
Parameters:
locale(str, default"en_US"): Determines the cultural context — names, phone formats, currency, banks, addresses, ID formats, payment methods, car brands, blood type distributions, and everything else. Use codes like"hi_IN","ja_JP","de_DE".seed(int or None): When set, the same seed always produces identical data. Critical for reproducible test fixtures.World(seed=42)will generate the exact same people, companies, and transactions every time.year(int, default2025): The reference year. Ages, dates of birth, graduation years, transaction dates, and vehicle model years are all calculated relative to this.industry(str or None): Sets the default industry context. Affects company names, job titles, product names, and salary ranges. Options:"tech","finance","health","retail","food","manufacturing".
Person
A person has 25+ fields, all coherent within the locale:
person = world.person()
# --- Identity ---
person.id # UUID
person.first_name # Locale-appropriate first name
person.last_name # Locale-appropriate last name
person.full_name # Ordered correctly (family-first in Japan/Korea)
person.gender # "male" or "female"
person.age # Bell curve centered at 35
person.date_of_birth # "1990-03-15" — consistent with age and year
person.nationality # "India", "Deutschland", "日本", etc.
person.national_id # Locale format: "9600 1338 9083" (Aadhaar)
person.marital_status # Age-dependent: "single", "married", "divorced", "widowed"
person.children # Locale-weighted: higher in Nigeria/Saudi, lower in Japan/Korea
# --- Contact ---
person.email # Derived from name + locale domain (yahoo.co.jp, gmail.com)
person.phone # Locale format: "+91 63794-02654"
person.phone_carrier # "Jio", "Airtel", "Verizon", "Docomo", etc.
person.username # Derived from name: "pooja.reddy128"
person.address # Address object with street, city, postal_code, country
person.address.street # Locale street format: "1675, Nehru Nagar"
person.address.city # "Pune", "München", "東京"
person.address.postal_code # Locale format: "559407", "SW1A 1AA"
# --- Financial ---
person.bank # Locale bank: "HDFC Bank", "三菱UFJ銀行"
person.credit_card_type # Locale-weighted: "RuPay" in India, "JCB" in Japan
person.salary # Log-normal distribution, locale-scaled
person.salary_currency # "INR", "EUR", "USD"
# --- Employment ---
person.job_title # Industry-appropriate: "Software Engineer", "Portfolio Manager"
person.department # "Engineering", "Finance", "Marketing"
person.company_id # Links to company (set by company.hire())
# --- Education ---
person.education.university # Locale university: "IIT Delhi", "MIT", "東京大学"
person.education.degree # Weighted: "Bachelor's" (55%), "Master's" (25%), "PhD" (5%)
person.education.field_of_study # "Computer Science", "Economics", etc.
person.education.graduation_year # Consistent with age and degree type
# --- Vehicle (70% chance) ---
person.vehicle # None if no vehicle
person.vehicle.brand # Locale-weighted: "Maruti Suzuki" in India, "Toyota" in Japan
person.vehicle.year # Recent model year
person.vehicle.plate_number # Locale format: "CH 16 IO 5255"
# --- Medical ---
person.medical.blood_type # Real regional distributions (O+ 51% in Nigeria, A+ 40% in Japan)
person.medical.height_cm # Bell curve by locale and gender
person.medical.weight_kg # Bell curve by locale and gender
person.medical.insurance_provider # "Star Health" (India), "NHS" (UK), "Blue Cross" (US)
Override any field:
# Force specific values
person = world.person(
gender="female",
age=30,
job_title="CEO",
salary=500000,
)
Company
company = world.company()
company.id # UUID
company.name # Industry + locale appropriate: "CloudPrime GmbH"
company.industry # "tech", "finance", "health", etc.
company.founded_year # Random year between 1970 and now
company.employee_count # 5, 12, 25, 50, 100, 250, 500, or 1000
company.currency # Locale currency
company.bank # Locale bank
company.city # Locale city
company.country # Locale country name
company.tax_id # Locale format: "DE960013389" (German VAT), "##-#######" (US EIN)
company.annual_revenue # Scales with employee count, log-normal
company.website # Derived from company name
company.departments # List of departments, scales with size
company.employees # List of hired Person objects
Hiring employees:
company = world.company(industry="tech")
team = company.hire(10) # 10 employees linked to this company
for person in team:
print(person.company_id) # == company.id
print(person.job_title) # Tech job titles
print(person.address.country) # Same locale as company
Product
product = world.product(company=company)
product.id # UUID
product.name # Industry-appropriate: "Cloud Platform", "Vita Formula"
product.sku # "CLO-4832"
product.price # Industry-appropriate range
product.currency # Locale currency
product.category # Matches industry
product.company_id # Links to company
Transaction
tx = world.transaction(sender=person, receiver=company)
tx.id # UUID
tx.type # "purchase", "refund", "subscription", "transfer", "payment", "invoice"
tx.amount # Log-normal: many small, few large
tx.tax_amount # Locale tax rate applied (18% GST in India, 19% in Germany, 8% in US)
tx.total_amount # amount + tax
tx.currency # Locale currency
tx.status # Weighted: 70% completed, 15% pending, 10% failed, 5% cancelled
tx.date # Random date within the context year
tx.payment_method # Locale-weighted: "UPI" (India), "Pix" (Brazil), "EC Card" (Germany)
tx.invoice_number # "INV-2025-74042"
tx.sender_id # Links to sender
tx.receiver_id # Links to receiver
tx.product_id # Links to product (optional)
tx.description # Human-readable summary
Context and Child Contexts
The Context is the engine behind coherence. You usually don't create one directly — World handles it. But you can create child contexts for scenarios like multinational companies:
# Japanese HQ
world = World(locale="ja_JP", seed=42, industry="tech")
hq = world.company()
# American branch — child context overrides locale but inherits industry
us_ctx = world.ctx.child(locale="en_US")
branch_employees = [Person.generate(us_ctx, company_id=hq.id) for _ in range(5)]
# These employees have American names/phones but work for the Japanese company
Full Dataset Export
Everything you generate is tracked and exportable:
world = World(locale="hi_IN", seed=42)
company = world.company(industry="tech")
company.hire(50)
world.products(10, company=company)
world.transactions(200)
# As a Python dict
data = world.dataset()
# {
# "metadata": {"locale": "hi_IN", "seed": 42, "year": 2025, "industry": None},
# "people": [...], # 50 people
# "companies": [...], # 1 company
# "products": [...], # 10 products
# "transactions": [...] # 200 transactions
# }
# As a JSON string
json_str = world.to_json(indent=2)
# Write to file
with open("test_fixtures.json", "w") as f:
f.write(world.to_json())
Overrides
Every generator accepts **overrides to force specific values:
# Override person fields
person = world.person(
first_name="Raj",
age=28,
gender="male",
salary=1200000,
job_title="CTO",
marital_status="single",
)
# Override company fields
company = world.company(
name="Acme Corp",
industry="tech",
employee_count=500,
founded_year=2010,
)
# Override transaction fields
tx = world.transaction(
type="purchase",
amount=9999.99,
payment_method="UPI",
status="completed",
)
Realistic Distributions
fabrikate doesn't use uniform random — it uses real-world statistical distributions:
- Names: Common names appear more frequently (skewed distribution)
- Ages: Bell curve centered at 35 for working populations
- Salaries: Log-normal (many average, few very high) scaled per locale
- Transaction amounts: Log-normal (many small purchases, few large ones)
- Blood types: Real regional frequencies (O+ is 51% in Nigeria, A+ is 40% in Japan)
- Children: Locale-weighted (avg 4-5 in Nigeria/Saudi, 1-2 in Japan/Korea)
- Marital status: Age-dependent (younger = more single, older = more married/widowed)
- Car brands: Market-share weighted (Maruti 35% in India, Toyota 30% in Japan)
- Payment methods: Usage-share weighted (UPI 45% in India, Pix 40% in Brazil)
- Credit cards: Regional preference (RuPay 35% in India, JCB 35% in Japan)
Locale-Specific Data
Each locale includes culturally accurate data for:
| Data Point | Example (India) | Example (Japan) | Example (Germany) |
|---|---|---|---|
| Phone format | +91 #####-##### | +81 ##-####-#### | +49 ### ####### |
| National ID | Aadhaar (12 digits) | My Number (12 digits) | Personalausweis |
| Tax ID | GSTIN | Corporate Number | USt-IdNr (DE#########) |
| Top bank | HDFC Bank | 三菱UFJ銀行 | Deutsche Bank |
| Top carrier | Jio | NTT Docomo | Telekom |
| Top car brand | Maruti Suzuki (35%) | Toyota (30%) | Volkswagen (22%) |
| Payment method | UPI (45%) | Credit Card (30%) | Bank Transfer (30%) |
| Credit card | RuPay (35%) | JCB (35%) | Girocard (20%) |
| Blood type O+ | 36.5% | 30% | 35% |
| Avg children | 2-3 | 1-2 | 1-2 |
| Salary range | ₹3L – ₹35L | ¥3M – ¥15M | €30K – €140K |
| Universities | IIT Bombay, BITS | 東京大学, 早稲田 | TU München, RWTH |
| Insurance | Star Health, LIC | National Health | TK, AOK, Barmer |
| Company suffix | Pvt. Ltd., LLP | 株式会社 | GmbH, AG |
| Street format | MG Road, Nehru Nagar | 渋谷区本町3丁目 | Goethestraße 42 |
Supported Industries
Six industries with appropriate job titles, company names, product names, and price ranges:
| Industry | Job Titles | Company Keywords | Product Price Range |
|---|---|---|---|
tech |
Software Engineer, CTO, Data Scientist | Cloud, Digital, AI, Quantum | $29.99 – $999.99 |
finance |
Financial Analyst, Portfolio Manager | Capital, Trust, Equity | $9.99 – $499.99 |
health |
Doctor, Pharmacist, Surgeon | Health, Vita, Bio, Care | $12.99 – $149.99 |
retail |
Store Manager, Buyer, E-commerce Manager | Market, Store, Fresh | $4.99 – $299.99 |
food |
Head Chef, Food Scientist, Sommelier | Fresh, Harvest, Kitchen | $3.99 – $79.99 |
manufacturing |
Production Manager, Quality Engineer | Steel, Forge, Precision | $99.99 – $9,999.99 |
Use Cases
Test fixtures: Generate deterministic, realistic test data for your app:
world = World(locale="en_US", seed=42)
users = world.people(100)
# Same 100 users every time, with coherent data
Database seeding: Populate a dev database with realistic data:
world = World(locale="hi_IN", seed=1, industry="tech")
for _ in range(5):
company = world.company()
company.hire(20)
world.transactions(500)
data = world.dataset()
# Insert into your DB
Demo environments: Create convincing demo data for presentations:
world = World(locale="de_DE", industry="finance")
company = world.company(name="FinanzPrime GmbH", employee_count=250)
company.hire(50)
Load testing: Generate large volumes of realistic data:
world = World(locale="en_US", seed=1)
people = world.people(10000) # 10K people, 99%+ unique names
txns = world.transactions(100000) # 100K transactions
API mocking: Return realistic responses in mock APIs:
world = World(locale="ja_JP", seed=1)
person = world.person()
return {"user": person.to_dict()}
Requirements
- Python 3.9+
- Zero external dependencies
License
MIT — Sai Aditya Datta Tipirneni
Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fabrikate-0.3.1.tar.gz.
File metadata
- Download URL: fabrikate-0.3.1.tar.gz
- Upload date:
- Size: 58.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d88f67e7ab724b8c1c09f52181bd081d8bf30731bfa560cc6e196f59b4acfb44
|
|
| MD5 |
80a1af0efe9388fbc5d5f16ab079ae88
|
|
| BLAKE2b-256 |
29328bb1175a87cc353a779f7c1d0423a44dbd9b5365780952dfd176db70d7de
|
File details
Details for the file fabrikate-0.3.1-py3-none-any.whl.
File metadata
- Download URL: fabrikate-0.3.1-py3-none-any.whl
- Upload date:
- Size: 56.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11a5323f5e08b894251de5455173376fa76c1acbf2010d8975f4e343066a7441
|
|
| MD5 |
f2c08502351698d142a5779bbf50799c
|
|
| BLAKE2b-256 |
93cf30748e44046f83163e6646d9ebd30ac22391d84fae7b42b0c997cde07c50
|