Skip to content

Commit 320e4d0

Browse files
jqueguinerclaude
andcommitted
docs: top-level README, repo reorg, sdist manifest
Add README.md orienting the three parts (Python naming feature, name dataset, Rust port). Ship parquet + rust sources in sdist (MANIFEST.in), ignore rust/target. Add scripts/bench_naming.py (Python perf harness). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 97e42d2 commit 320e4d0

4 files changed

Lines changed: 112 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,6 @@ nosetests.xml
5353
*.ipr
5454
venv/
5555
.vscode
56+
57+
# Rust port build artifacts
58+
rust/target/

MANIFEST.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
include README.md
12
include README.rst
23
include README.upstream.rst
4+
include data/*.parquet
5+
include data/README.md
6+
recursive-include rust *.rs *.toml *.md
7+
recursive-exclude rust/target *
38
include LICENSE.txt
49
include CONTRIBUTING.rst
510
include CHANGELOG.md

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# faker2 — fork with name intelligence + a Rust port
2+
3+
This fork of [faker2](README.upstream.rst) adds three things on top of upstream:
4+
5+
1. **Name intelligence** (`faker2/naming/`) — gender inference, gender-preserving
6+
name replacement, and English grammatical-number agreement.
7+
2. **A real name ground-truth dataset** (`data/`) — 1.43M first names across 139
8+
countries with gender + relative frequency, as Parquet.
9+
3. **A Rust port** (`rust/`) — the core engine + key providers + the same name
10+
intelligence, re-implemented in Rust.
11+
12+
Upstream faker docs: [`README.upstream.rst`](README.upstream.rst).
13+
14+
## Repository layout
15+
16+
```
17+
faker2/ Python package (upstream faker fork)
18+
naming/ ← added: name intelligence
19+
gender.py gender infer + preserving replace (bundled locales)
20+
realnames.py same, backed by data/ ground truth (needs pyarrow)
21+
grammar.py pluralize / singularize / agreement
22+
data/ ← added: name ground truth
23+
first_names.parquet 1.43M names, 139 countries, gender, relative frequency
24+
last_names.parquet small sample (surnames NOT comprehensive)
25+
README.md schema + provenance
26+
rust/ ← added: Rust port
27+
src/ engine, providers, gender, grammar, realnames
28+
README.md crate docs
29+
tests/ Python tests (incl. test_gender_grammar.py)
30+
```
31+
32+
## Python — name intelligence
33+
34+
```python
35+
from faker2.naming import realnames, grammar
36+
37+
realnames.infer_gender("Jacques", "FR") # "m"
38+
realnames.first_name_like("Jacques", "FR") # frequency-weighted male FR name
39+
realnames.first_name("JP", "f") # weighted female Japanese name
40+
41+
grammar.pluralize("baby") # "babies"
42+
grammar.agree(3, "dog") # "3 dogs"
43+
```
44+
45+
`realnames` needs `pyarrow` (in `dev-requirements.txt`). The bundled-locale
46+
variant `faker2.naming.gender` has no extra dependency.
47+
48+
## Rust port
49+
50+
```
51+
cd rust
52+
cargo test # zero-dependency core
53+
cargo run -- --seed 42 name
54+
cargo run -- like Jacques fr # gender-preserving replacement
55+
cargo test --features real-names # opt-in parquet ground truth
56+
```
57+
58+
See [`rust/README.md`](rust/README.md).
59+
60+
## Data provenance
61+
62+
The name dataset was extracted from a PostgreSQL dump and normalized so
63+
`frequency` is a **relative share** — no raw population counts are exposed. See
64+
[`data/README.md`](data/README.md).

scripts/bench_naming.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Python perf harness — mirror of rust/examples/bench.rs."""
2+
import time
3+
4+
from faker2 import Faker
5+
from faker2.naming import realnames as rn
6+
7+
f = Faker()
8+
Faker.seed(42)
9+
10+
# 1) basic name generation (upstream faker)
11+
n = 1_000_000
12+
t = time.perf_counter()
13+
sink = 0
14+
for _ in range(n):
15+
sink += len(f.first_name())
16+
d = time.perf_counter() - t
17+
print(f"basic first_name(): {n} in {d:.3f}s = {n/d/1e6:.3f} M ops/s (sink {sink})")
18+
19+
# 2) real-names bank load
20+
t = time.perf_counter()
21+
rn.infer_gender("Jacques", "FR") # triggers load+build
22+
print(f"real bank load: {time.perf_counter()-t:.3f}s")
23+
24+
# 3) gender-preserving replacement
25+
m = 1_000_000
26+
t = time.perf_counter()
27+
s2 = 0
28+
for _ in range(m):
29+
s2 += len(rn.first_name_like("Jacques", "FR"))
30+
d = time.perf_counter() - t
31+
print(f"first_name_like: {m} in {d:.3f}s = {m/d/1e6:.3f} M ops/s (sink {s2})")
32+
33+
# 4) infer only
34+
t = time.perf_counter()
35+
s3 = 0
36+
for _ in range(m):
37+
if rn.infer_gender("Jacques", "FR") == "m":
38+
s3 += 1
39+
d = time.perf_counter() - t
40+
print(f"infer_gender: {m} in {d:.3f}s = {m/d/1e6:.3f} M ops/s (sink {s3})")

0 commit comments

Comments
 (0)