fauxdata-cli 0.1.5__tar.gz → 0.2.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.
Files changed (45) hide show
  1. fauxdata_cli-0.2.0/LOG.md +84 -0
  2. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/PKG-INFO +50 -4
  3. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/README.md +47 -1
  4. fauxdata_cli-0.2.0/RELEASING.md +83 -0
  5. fauxdata_cli-0.2.0/docs/future-ideas.md +8 -0
  6. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/docs/index.html +12 -4
  7. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/pyproject.toml +2 -2
  8. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/__init__.py +1 -1
  9. fauxdata_cli-0.2.0/src/fauxdata/commands/agent.py +160 -0
  10. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/generate.py +34 -2
  11. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/infer.py +37 -9
  12. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/preview.py +38 -24
  13. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/validate.py +22 -6
  14. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/generator.py +25 -2
  15. fauxdata_cli-0.2.0/src/fauxdata/jsonmode.py +19 -0
  16. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/main.py +34 -6
  17. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/schema.py +1 -0
  18. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/validator.py +28 -18
  19. fauxdata_cli-0.2.0/tasks/todo.md +90 -0
  20. fauxdata_cli-0.2.0/tests/test_agent.py +163 -0
  21. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_cli.py +13 -0
  22. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_generator.py +39 -0
  23. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_infer.py +51 -0
  24. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_schema.py +11 -0
  25. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_validator.py +67 -1
  26. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/uv.lock +446 -8
  27. fauxdata_cli-0.1.5/.claude/settings.local.json +0 -11
  28. fauxdata_cli-0.1.5/LOG.md +0 -57
  29. fauxdata_cli-0.1.5/share.png +0 -0
  30. fauxdata_cli-0.1.5/tasks/todo.md +0 -52
  31. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/.gitignore +0 -0
  32. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/.python-version +0 -0
  33. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/LICENSE +0 -0
  34. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/docs/deployment.md +0 -0
  35. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/docs/share.png +0 -0
  36. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/schemas/events.yml +0 -0
  37. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/schemas/orders.yml +0 -0
  38. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/schemas/people.yml +0 -0
  39. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/__init__.py +0 -0
  40. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/commands/init.py +0 -0
  41. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/src/fauxdata/output.py +0 -0
  42. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/__init__.py +0 -0
  43. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/conftest.py +0 -0
  44. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_new_fields.py +0 -0
  45. {fauxdata_cli-0.1.5 → fauxdata_cli-0.2.0}/tests/test_output.py +0 -0
@@ -0,0 +1,84 @@
1
+ # Log
2
+
3
+ ## 2026-10-11 — 0.2.0 (non rilasciata): pointblank 1.0.1, `precision` nativa, `col_vals_str_len`, fix, comando `agent` e `--json`
4
+
5
+ - Bump `pointblank>=1.0.1` (da 0.25.0). 103/103 test invariati; schemi inclusi PASS; stesso seed → CSV identici byte per byte tra 0.25 e 1.0.1. Effetto collaterale: da 0.27 pointblank richiede `anthropic[bedrock]` → 20 pacchetti in più installati (anthropic, boto3, pydantic, …), non usati da fauxdata.
6
+ - `precision` float: ora nativa (`pb.float_field(precision=)`), unicità garantita dopo l'arrotondamento. Limiti stretti verso l'interno sulla griglia dei decimali (`max: 9.999`, `precision: 2` → `9.99`) perché l'arrotondamento non sfori il range; errore chiaro se nessun valore ci sta. Documentata nel README (prima non lo era).
7
+ - Nuova regola `col_vals_str_len` (`column`, `min`/`max`, null ignorati). `infer` la emette solo per stringhe libere: su preset/pattern/values il generatore ignora le lunghezze.
8
+ - Fix: le regole sui valori (`between`, `gt/lt/ge/le`, `regex`, `in_set`) ignorano i null (`na_pass=True`; `in_set` via `None` nel set). Prima una colonna col 20% di null falliva 195/1000 righe.
9
+ - Fix: `infer --categorical-threshold` da CLI arrivava come float (anche il default) e pointblank lo leggeva come frazione → ogni colonna stringa senza preset diventava categorica con i valori reali copiati in `values:`. Ora gli interi ≥ 1 passano come conteggio.
10
+ - Fix: `values` sulle colonne `int`/`float` ora rispettato in generazione (passato come `allowed`); prima `infer` lo emetteva per gli int a bassa cardinalità e il generatore lo ignorava (403/500 fallimenti in round-trip).
11
+ - Fix: `rows_distinct` esclude le righe con null nelle colonne controllate (come SQL UNIQUE); prima i null contavano come duplicati tra loro.
12
+ - Bug di pointblank (non nostro): `unique=True` ignorato sui preset coerenti (`email`, `name`, `city`, …); segnalato in https://github.com/posit-dev/pointblank/issues/441. Non corretto: `unique` + `nullable` su range stretto non generabile.
13
+ - Nuovo comando `fauxdata agent`: guida Markdown per agenti AI (flusso, output JSON, exit code, schema, tipi, preset, regole, esempio); liste generate dalle costanti di `schema.py`.
14
+ - Nuovo flag `--json` su `generate`, `validate`, `infer`, `preview`: stdout contiene solo un oggetto JSON; errori come `{"error": ...}` con exit 1. Sezione README "Using fauxdata with AI agents".
15
+ - Fix: un passo su una colonna assente dal dataset risultava `ok: true` con 0/0 righe; ora campo `evaluated` (da `validation_info`), `ok: false`, `NOT RUN` in tabella. `get_dataframe_report()` non usabile: fallisce con `rows_distinct`/`rows_complete` + `columns_subset` (segnalato in https://github.com/posit-dev/pointblank/issues/442). Chiude la issue #1 di fauxdata (output strutturato → `--json`).
16
+ - Sito `docs/index.html`: badge pointblank 1.0+, "six commands", righe `infer` e `agent`, `--json`.
17
+ - 143/143 test, coverage 87.05%.
18
+
19
+ ## 2026-07-04 — v0.1.6
20
+
21
+ - Release `0.1.6`: include il fix issue #2 (`precision` float). Build con `uv build`, tag `v0.1.6`.
22
+ - Deploy PyPI **manuale** (`uv publish`): nessun workflow di release automatico.
23
+
24
+ ## 2026-07-04 — fix issue #2: `precision` float ignorato
25
+
26
+ - Bug: l'opzione `precision` dei float veniva letta nello schema ma mai applicata (`pb.float_field` non ha il parametro). Valori generati con precisione piena.
27
+ - Fix in `generator.py`: dopo `pb.generate_dataset`, arrotondo con `pl.col(...).round(precision)` le colonne `float` con `precision` impostato.
28
+ - Verifica: reproducer della issue → output atteso (`27.74, 169.64, …`). Test `test_generate_float_precision`. 103/103 pass.
29
+
30
+ ## 2026-06-12 — pointblank 0.25 + comando `infer`
31
+
32
+ - Bump `pointblank>=0.25` (uscito su PyPI). Schemi inclusi (people/orders/events) verificati: tutti PASS.
33
+ - Fase B regressioni: `rows_distinct` ora include righe con null (correttezza migliorata), lazy frame ok. fauxdata passa sempre DataFrame eager → nessun impatto.
34
+ - Nuovo comando **`fauxdata infer DATASET`**: da tabella reale (csv/parquet/json/jsonl) genera schema YAML (`columns:` + `validation:`) via `pb.schema_from_tbl`. Flag: `--out/-o`, `--name/-n`, `--rows/-r`, `--format/-f`, `--categorical-threshold`, `--detect-presets/--no-detect-presets`, `--sample-size`.
35
+ - `ColumnSchema`: nuovi campi `min_length`/`max_length` per stringhe (round-trip fedele); passati a `pb.string_field`. Validazione parsing (negativi, min>max).
36
+ - infer CSV: `try_parse_dates=True` → date/datetime tipizzati invece che congelati come categorici.
37
+ - generate: errore chiaro su conflitto unique+range stretto ("too narrow a range") invece del traceback criptico di pointblank.
38
+ - Limite noto: colonne a bassa cardinalità congelate ai valori reali (possibile leak su colonne sensibili) — documentato nel README.
39
+ - Test: +14 (`tests/test_infer.py` + length in `test_new_fields.py`). 102/102 pass, coverage 83.2%.
40
+
41
+ - `init`: aggiunto `--description`, `--rows`, `--format`, `--yes`; questionary diventa fallback
42
+ - Tutti i comandi: `epilog` con esempi in `--help`
43
+ - `generate`: aggiunto `--dry-run` (mostra piano senza scrivere)
44
+ - `generate`: output su successo mostra `output_path`, `format`, `rows` come chiave: valore
45
+ - `init`: output su successo aggiunge `schema_path: <file>`
46
+ - Fix venv: ricreato con `uv venv --clear` (cartella rinominata da `real_fake_datasets`)
47
+
48
+ ## 2026-03-06 — v0.1.3
49
+
50
+ - Add Python classifiers to pyproject.toml (3.11, 3.12, 3.13, MIT) — fixes pyversions badge
51
+ - Fix version tests to read `__version__` dynamically instead of hardcoded string
52
+
53
+ ## 2026-03-06 — v0.1.2
54
+
55
+ - Bump to 0.1.2 and publish to PyPI
56
+
57
+ ## 2026-03-06 (feature)
58
+
59
+ - `--version` / `-V` flag nel CLI (`fauxdata --version` → `fauxdata 0.1.1`)
60
+ - Coverage threshold 80% in pytest config (`--cov-fail-under=80`); attuale: 83.76%
61
+ - Nuovo campo `pattern` in ColumnSchema: genera stringhe che matchano un regex via pointblank
62
+ - Nuovo campo `null_probability` in ColumnSchema: controllo granulare dei null (0.0–1.0), con validazione in parsing
63
+ - Rimossa dipendenza `faker` (non usata, pointblank gestisce tutto)
64
+ - Fix generator: `null_probability=None` non passato a pointblank (causa TypeError)
65
+ - Test aggiornati: 79/79 pass
66
+
67
+ ## 2026-03-06 (tests)
68
+
69
+ - Add pytest test suite: 65 tests, 100% pass, 0.44s
70
+ - `tests/test_schema.py`: unit tests for YAML schema parsing (valid/invalid cases)
71
+ - `tests/test_output.py`: unit tests for export functions (all formats, stdout, errors)
72
+ - `tests/test_generator.py`: integration tests for generation (types, seed, unique, presets)
73
+ - `tests/test_validator.py`: integration tests for validation rules (pass/fail scenarios)
74
+ - `tests/test_cli.py`: CLI smoke tests via `typer.testing.CliRunner`
75
+ - Add `[dependency-groups] dev` in `pyproject.toml` (pytest, pytest-cov); config via `[tool.pytest.ini_options]`
76
+
77
+ ## 2026-03-06
78
+
79
+ - Initial implementation of `fauxdata` CLI
80
+ - Stack: pointblank 0.22 (native generation + validation), polars, typer, rich, pyfiglet, questionary
81
+ - Commands: `init`, `generate`, `validate`, `preview`
82
+ - Example schemas: `people.yml`, `orders.yml`, `events.yml`
83
+ - All schemas generate and validate cleanly (all rules PASS)
84
+ - `locale` field at schema level maps to pointblank `country=` param
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: fauxdata-cli
3
- Version: 0.1.5
3
+ Version: 0.2.0
4
4
  Summary: CLI for generating and validating fake datasets
5
5
  Project-URL: Homepage, https://aborruso.github.io/fauxdata/
6
6
  Project-URL: Repository, https://github.com/aborruso/fauxdata
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Requires-Python: >=3.11
16
- Requires-Dist: pointblank>=0.25
16
+ Requires-Dist: pointblank>=1.0.1
17
17
  Requires-Dist: polars>=1.0
18
18
  Requires-Dist: pyfiglet>=1.0
19
19
  Requires-Dist: pyyaml>=6.0
@@ -170,12 +170,26 @@ validation:
170
170
  | Type | Description | Options |
171
171
  |------|-------------|---------|
172
172
  | `int` | Integer | `min`, `max`, `unique` |
173
- | `float` | Floating point | `min`, `max` |
173
+ | `float` | Floating point | `min`, `max`, `precision` |
174
174
  | `string` | Text | `preset`, `values`, `pattern`, `unique`, `min_length`, `max_length` |
175
175
  | `bool` | Boolean | — |
176
176
  | `date` | Date | `min`, `max` (ISO format) |
177
177
  | `datetime` | Datetime | `min`, `max` (ISO format) |
178
178
 
179
+ ### Float precision
180
+
181
+ `precision` sets the number of decimal places of a `float` column, e.g. `precision: 2` for prices:
182
+
183
+ ```yaml
184
+ price:
185
+ type: float
186
+ min: 1.0
187
+ max: 199.999
188
+ precision: 2
189
+ ```
190
+
191
+ Rounding never pushes a value outside `min`/`max`. When a bound has more decimals than `precision`, fauxdata moves it inward to the nearest value that fits: in the example above `max: 199.999` becomes `199.99`, so no generated price can round up to `200.00`. If no value with the requested decimals fits between `min` and `max` (e.g. `min: 1.001`, `max: 1.009`, `precision: 2`), `generate` stops with an error instead of producing out-of-range data. With `unique: true`, values are unique after rounding.
192
+
179
193
  ### String presets
180
194
 
181
195
  Presets generate realistic, locale-aware values. Set `locale` at the schema level to control the country.
@@ -230,6 +244,7 @@ Supported locales include: `US`, `IT`, `DE`, `FR`, `ES`, `JP`, `BR`, `PL`, `NL`,
230
244
  | `col_vals_between` | Value in range | `column`, `min`, `max` |
231
245
  | `col_vals_regex` | Matches pattern | `column`, `pattern` |
232
246
  | `col_vals_in_set` | Value in allowed set | `column`, `values` |
247
+ | `col_vals_str_len` | String length in range (nulls are ignored) | `column`, `min` and/or `max` |
233
248
  | `col_vals_gt` / `col_vals_lt` | Greater / less than | `column`, `min` / `max` |
234
249
  | `col_vals_ge` / `col_vals_le` | Greater / less or equal | `column`, `min` / `max` |
235
250
  | `rows_distinct` | Unique rows | `columns` |
@@ -256,6 +271,7 @@ fauxdata generate schemas/people.yml --out - --format jsonl | wc -l
256
271
  | `--format` | `-f` | from schema | Output format: `csv`, `parquet`, `json`, `jsonl`, `jsonlines` |
257
272
  | `--seed` | `-s` | from schema | Random seed for reproducibility |
258
273
  | `--validate` | `-v` | off | Run validation rules after generating |
274
+ | `--json` | | off | Print only a JSON summary to stdout (see [Using fauxdata with AI agents](#using-fauxdata-with-ai-agents)) |
259
275
 
260
276
  When `--out -` is used, all output messages are suppressed and only data is written to stdout.
261
277
 
@@ -317,6 +333,36 @@ Interactive wizard to create a new schema template. Asks for name, description,
317
333
 
318
334
  ---
319
335
 
336
+ ## Using fauxdata with AI agents
337
+
338
+ fauxdata is designed to be driven by AI agents as well as by people.
339
+
340
+ **Start with `fauxdata agent`.** It prints a Markdown guide with everything an agent needs to use the tool without reading this README: the workflow, the schema format, column types and options, presets, validation rules, the JSON output of each command, exit codes and a complete example schema. The lists of types, presets and rules are built from the code, so the guide always matches the installed version.
341
+
342
+ ```
343
+ fauxdata agent > fauxdata-guide.md
344
+ ```
345
+
346
+ **Add `--json` to get machine-readable results.** `generate`, `validate`, `infer` and `preview` accept `--json`: stdout then contains a single JSON object and nothing else (no panels, tables or colors).
347
+
348
+ ```
349
+ fauxdata infer real.csv --out schema.yml --json
350
+ {"schema_path": "schema.yml", "columns": ["id", "email", "age"], "validation_rules": 4}
351
+
352
+ fauxdata generate schema.yml --out data.csv --validate --json
353
+ {"output_path": "data.csv", "format": "csv", "rows": 1000, "seed": 42, "dry_run": false, "validation": {"passed": true, "results": [...]}}
354
+
355
+ fauxdata validate data.csv schema.yml --json
356
+ {"passed": true, "rows": 1000, "results": [{"step": 1, "rule": "col_vals_not_null", "column": "id", "passed": 1000, "failed": 0, "total": 1000, "ok": true, "evaluated": true}, ...]}
357
+ ```
358
+
359
+ - Errors come back as `{"error": "..."}` on stdout with exit code `1`.
360
+ - A step that could not run (for example, its column is missing from the data) has `"evaluated": false` and `"ok": false`; the human-readable table shows it as `NOT RUN`.
361
+ - Exit codes: `0` success, `1` error or at least one validation rule failed.
362
+ - `--json` cannot be combined with `--out -`, which already writes data (or YAML, for `infer`) to stdout.
363
+
364
+ ---
365
+
320
366
  ## Example schemas
321
367
 
322
368
  Three ready-to-use schemas are included in `schemas/`:
@@ -146,12 +146,26 @@ validation:
146
146
  | Type | Description | Options |
147
147
  |------|-------------|---------|
148
148
  | `int` | Integer | `min`, `max`, `unique` |
149
- | `float` | Floating point | `min`, `max` |
149
+ | `float` | Floating point | `min`, `max`, `precision` |
150
150
  | `string` | Text | `preset`, `values`, `pattern`, `unique`, `min_length`, `max_length` |
151
151
  | `bool` | Boolean | — |
152
152
  | `date` | Date | `min`, `max` (ISO format) |
153
153
  | `datetime` | Datetime | `min`, `max` (ISO format) |
154
154
 
155
+ ### Float precision
156
+
157
+ `precision` sets the number of decimal places of a `float` column, e.g. `precision: 2` for prices:
158
+
159
+ ```yaml
160
+ price:
161
+ type: float
162
+ min: 1.0
163
+ max: 199.999
164
+ precision: 2
165
+ ```
166
+
167
+ Rounding never pushes a value outside `min`/`max`. When a bound has more decimals than `precision`, fauxdata moves it inward to the nearest value that fits: in the example above `max: 199.999` becomes `199.99`, so no generated price can round up to `200.00`. If no value with the requested decimals fits between `min` and `max` (e.g. `min: 1.001`, `max: 1.009`, `precision: 2`), `generate` stops with an error instead of producing out-of-range data. With `unique: true`, values are unique after rounding.
168
+
155
169
  ### String presets
156
170
 
157
171
  Presets generate realistic, locale-aware values. Set `locale` at the schema level to control the country.
@@ -206,6 +220,7 @@ Supported locales include: `US`, `IT`, `DE`, `FR`, `ES`, `JP`, `BR`, `PL`, `NL`,
206
220
  | `col_vals_between` | Value in range | `column`, `min`, `max` |
207
221
  | `col_vals_regex` | Matches pattern | `column`, `pattern` |
208
222
  | `col_vals_in_set` | Value in allowed set | `column`, `values` |
223
+ | `col_vals_str_len` | String length in range (nulls are ignored) | `column`, `min` and/or `max` |
209
224
  | `col_vals_gt` / `col_vals_lt` | Greater / less than | `column`, `min` / `max` |
210
225
  | `col_vals_ge` / `col_vals_le` | Greater / less or equal | `column`, `min` / `max` |
211
226
  | `rows_distinct` | Unique rows | `columns` |
@@ -232,6 +247,7 @@ fauxdata generate schemas/people.yml --out - --format jsonl | wc -l
232
247
  | `--format` | `-f` | from schema | Output format: `csv`, `parquet`, `json`, `jsonl`, `jsonlines` |
233
248
  | `--seed` | `-s` | from schema | Random seed for reproducibility |
234
249
  | `--validate` | `-v` | off | Run validation rules after generating |
250
+ | `--json` | | off | Print only a JSON summary to stdout (see [Using fauxdata with AI agents](#using-fauxdata-with-ai-agents)) |
235
251
 
236
252
  When `--out -` is used, all output messages are suppressed and only data is written to stdout.
237
253
 
@@ -293,6 +309,36 @@ Interactive wizard to create a new schema template. Asks for name, description,
293
309
 
294
310
  ---
295
311
 
312
+ ## Using fauxdata with AI agents
313
+
314
+ fauxdata is designed to be driven by AI agents as well as by people.
315
+
316
+ **Start with `fauxdata agent`.** It prints a Markdown guide with everything an agent needs to use the tool without reading this README: the workflow, the schema format, column types and options, presets, validation rules, the JSON output of each command, exit codes and a complete example schema. The lists of types, presets and rules are built from the code, so the guide always matches the installed version.
317
+
318
+ ```
319
+ fauxdata agent > fauxdata-guide.md
320
+ ```
321
+
322
+ **Add `--json` to get machine-readable results.** `generate`, `validate`, `infer` and `preview` accept `--json`: stdout then contains a single JSON object and nothing else (no panels, tables or colors).
323
+
324
+ ```
325
+ fauxdata infer real.csv --out schema.yml --json
326
+ {"schema_path": "schema.yml", "columns": ["id", "email", "age"], "validation_rules": 4}
327
+
328
+ fauxdata generate schema.yml --out data.csv --validate --json
329
+ {"output_path": "data.csv", "format": "csv", "rows": 1000, "seed": 42, "dry_run": false, "validation": {"passed": true, "results": [...]}}
330
+
331
+ fauxdata validate data.csv schema.yml --json
332
+ {"passed": true, "rows": 1000, "results": [{"step": 1, "rule": "col_vals_not_null", "column": "id", "passed": 1000, "failed": 0, "total": 1000, "ok": true, "evaluated": true}, ...]}
333
+ ```
334
+
335
+ - Errors come back as `{"error": "..."}` on stdout with exit code `1`.
336
+ - A step that could not run (for example, its column is missing from the data) has `"evaluated": false` and `"ok": false`; the human-readable table shows it as `NOT RUN`.
337
+ - Exit codes: `0` success, `1` error or at least one validation rule failed.
338
+ - `--json` cannot be combined with `--out -`, which already writes data (or YAML, for `infer`) to stdout.
339
+
340
+ ---
341
+
296
342
  ## Example schemas
297
343
 
298
344
  Three ready-to-use schemas are included in `schemas/`:
@@ -0,0 +1,83 @@
1
+ # Releasing
2
+
3
+ Procedura per pubblicare una nuova release di `fauxdata` su PyPI.
4
+ Il deploy **non è automatico**: nessun workflow GitHub Actions. Ogni step è manuale.
5
+
6
+ ## Versioning
7
+
8
+ `MAJOR.MINOR.PATCH` (SemVer).
9
+
10
+ - **subrelease / patch** (`0.1.5` → `0.1.6`): bugfix, nessuna modifica di API.
11
+ - **minor** (`0.1.x` → `0.2.0`): nuove feature retrocompatibili.
12
+ - **major** (`0.x` → `1.0.0`): breaking change.
13
+
14
+ ## Step
15
+
16
+ 1. **Aggiorna la versione** nei due file (devono restare allineati):
17
+ - `pyproject.toml` → `version = "X.Y.Z"`
18
+ - `src/fauxdata/__init__.py` → `__version__ = "X.Y.Z"`
19
+
20
+ 2. **Aggiorna `LOG.md`**: nuova sezione in cima con data `YYYY-MM-DD` e le modifiche.
21
+
22
+ 3. **Test**: devono passare tutti.
23
+
24
+ ```bash
25
+ uv run pytest tests/ -q
26
+ uv run fauxdata --version # deve stampare la nuova versione
27
+ ```
28
+
29
+ 4. **Build**:
30
+
31
+ ```bash
32
+ rm -rf dist/
33
+ uv build
34
+ ls dist/ # fauxdata_cli-X.Y.Z-py3-none-any.whl + .tar.gz
35
+ ```
36
+
37
+ 5. **Aggiorna la CLI installata localmente** (`fauxdata` è installato come `uv tool`):
38
+
39
+ ```bash
40
+ uv tool install --reinstall . # reinstalla dalla working copy
41
+ fauxdata --version # deve stampare la nuova versione
42
+ ```
43
+
44
+ 6. **Commit + tag + push**:
45
+
46
+ ```bash
47
+ git add pyproject.toml src/fauxdata/__init__.py LOG.md
48
+ git commit -m "chore: vX.Y.Z — <sintesi>"
49
+ git tag -a vX.Y.Z -m "vX.Y.Z — <sintesi>"
50
+ git push origin main
51
+ git push origin vX.Y.Z
52
+ ```
53
+
54
+ 7. **Pubblica su PyPI** (richiede token PyPI — step da eseguire a mano):
55
+
56
+ ```bash
57
+ uv publish # oppure: twine upload dist/*
58
+ ```
59
+
60
+ Il token si passa via `UV_PUBLISH_TOKEN` o `--token`. Non committare mai il token.
61
+
62
+ 8. **Verifica / aggiorna da PyPI** (il pacchetto su PyPI è `fauxdata-cli`):
63
+
64
+ ```bash
65
+ uvx fauxdata@X.Y.Z --version # verifica al volo la versione pubblicata
66
+
67
+ pip install --upgrade fauxdata-cli # aggiorna un'installazione pip esistente
68
+ # oppure, se installato come uv tool:
69
+ uv tool upgrade fauxdata-cli
70
+ ```
71
+
72
+ 9. **GitHub Release**: dal tag già pushato (il tag da solo **non** crea la release).
73
+ **Titolo e note devono essere in inglese.**
74
+
75
+ ```bash
76
+ gh release create vX.Y.Z --title "vX.Y.Z" --notes "<changelog in English>"
77
+ ```
78
+
79
+ ## Note
80
+
81
+ - Se una release chiude una issue, referenziarla nel commit/tag (`#N`) e chiudere la issue con un commento che linka al commit.
82
+ - La versione è testata dinamicamente (`test_cli_version` legge `__version__`), quindi il bump non rompe i test.
83
+ - **Tag ≠ GitHub Release**: `git push origin vX.Y.Z` crea solo il tag; la pagina di release va creata a parte con `gh release create` (step 9).
@@ -0,0 +1,8 @@
1
+ # Future ideas
2
+
3
+ Ideas from the pointblank 0.26-1.0.1 releases, not yet planned (2026-10-11).
4
+
5
+ - Related tables: generate linked datasets (e.g. people and orders) and check foreign keys with `col_vals_in_table()` (pointblank 0.27).
6
+ - Coded missing values: a schema field for sentinel values like `-99` or `N/A`, generated and validated via `MissingSpec` and `col_pct_missing()` (pointblank 0.26).
7
+ - Schema export: let `infer` also write JSON Schema, Frictionless, dbt or ODCS via `export_contract()` (pointblank 0.26/0.27).
8
+ - Fidelity report: compare the real dataset with the synthetic one using `DataScan.compare()` (PSI, Kolmogorov-Smirnov) to show how close the shapes are (pointblank 0.27).
@@ -788,7 +788,7 @@
788
788
  </div>
789
789
 
790
790
  <div class="badges" aria-label="Technologies used">
791
- <span class="badge green">pointblank 0.22+</span>
791
+ <span class="badge green">pointblank 1.0+</span>
792
792
  <span class="badge green">polars</span>
793
793
  <span class="badge amber">python 3.11+</span>
794
794
  <span class="badge blue">100+ locales</span>
@@ -858,7 +858,7 @@
858
858
  <section id="commands" aria-labelledby="commands-heading">
859
859
  <div class="container">
860
860
  <p class="section-label" aria-hidden="true">// the commands</p>
861
- <h2 id="commands-heading">four commands.<br>one tool.</h2>
861
+ <h2 id="commands-heading">six commands.<br>one tool.</h2>
862
862
 
863
863
  <div class="table-wrap">
864
864
  <table>
@@ -871,11 +871,15 @@
871
871
  <tbody>
872
872
  <tr>
873
873
  <td><code>fauxdata generate SCHEMA</code></td>
874
- <td>Generate a dataset from a YAML schema. Options: <code>--rows</code>, <code>--format</code>, <code>--seed</code>, <code>--out -</code> (stdout), <code>--validate</code></td>
874
+ <td>Generate a dataset from a YAML schema. Options: <code>--rows</code>, <code>--format</code>, <code>--seed</code>, <code>--out -</code> (stdout), <code>--validate</code>, <code>--json</code></td>
875
875
  </tr>
876
876
  <tr>
877
877
  <td><code>fauxdata validate DATASET SCHEMA</code></td>
878
- <td>Validate an existing file against a schema. Exits with code 1 on failure — CI-ready.</td>
878
+ <td>Validate an existing file against a schema. Exits with code 1 on failure — CI-ready. Add <code>--json</code> for machine-readable results.</td>
879
+ </tr>
880
+ <tr>
881
+ <td><code>fauxdata infer DATASET</code></td>
882
+ <td>Build a YAML schema from a real dataset: ranges, categories, presets, null rates, plus matching validation rules. Generate look-alike data without shipping the real values.</td>
879
883
  </tr>
880
884
  <tr>
881
885
  <td><code>fauxdata preview DATASET</code></td>
@@ -885,6 +889,10 @@
885
889
  <td><code>fauxdata init [--name]</code></td>
886
890
  <td>Interactive wizard to create a new schema template.</td>
887
891
  </tr>
892
+ <tr>
893
+ <td><code>fauxdata agent</code></td>
894
+ <td>Print a Markdown guide for AI agents: workflow, schema format, presets, rules, JSON output. <code>generate</code>, <code>validate</code>, <code>infer</code> and <code>preview</code> also speak <code>--json</code>.</td>
895
+ </tr>
888
896
  </tbody>
889
897
  </table>
890
898
  </div>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fauxdata-cli"
3
- version = "0.1.5"
3
+ version = "0.2.0"
4
4
  description = "CLI for generating and validating fake datasets"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -13,7 +13,7 @@ classifiers = [
13
13
  "Operating System :: OS Independent",
14
14
  ]
15
15
  dependencies = [
16
- "pointblank>=0.25",
16
+ "pointblank>=1.0.1",
17
17
  "typer>=0.12",
18
18
  "rich>=13",
19
19
  "pyfiglet>=1.0",
@@ -1,3 +1,3 @@
1
1
  """fauxdata - CLI for generating and validating fake datasets."""
2
2
 
3
- __version__ = "0.1.5"
3
+ __version__ = "0.2.0"
@@ -0,0 +1,160 @@
1
+ """fauxdata agent command - a Markdown guide for AI agents.
2
+
3
+ The lists of column types, presets and validation rules are built from the
4
+ constants in ``fauxdata.schema``, so the guide cannot drift from the code.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+ from fauxdata import __version__
12
+ from fauxdata.schema import STRING_PRESETS, VALID_RULES, VALID_TYPES
13
+
14
+ TYPE_OPTIONS = {
15
+ "int": "`min`, `max`, `unique`, `values`",
16
+ "float": "`min`, `max`, `precision`, `unique`, `values`",
17
+ "string": "`preset`, `values`, `pattern`, `unique`, `min_length`, `max_length`",
18
+ "bool": "-",
19
+ "date": "`min`, `max` (ISO date), `unique`",
20
+ "datetime": "`min`, `max` (ISO datetime), `unique`",
21
+ }
22
+
23
+ RULE_PARAMS = {
24
+ "col_vals_not_null": ("`columns`", "no nulls"),
25
+ "col_vals_between": ("`column`, `min`, `max`", "value in range"),
26
+ "col_vals_gt": ("`column`, `min`", "value > min"),
27
+ "col_vals_lt": ("`column`, `max`", "value < max"),
28
+ "col_vals_ge": ("`column`, `min`", "value >= min"),
29
+ "col_vals_le": ("`column`, `max`", "value <= max"),
30
+ "col_vals_regex": ("`column`, `pattern`", "value matches the regex"),
31
+ "col_vals_in_set": ("`column`, `values`", "value in the allowed set"),
32
+ "col_vals_str_len": ("`column`, `min` and/or `max`", "string length in range"),
33
+ "rows_distinct": ("`columns`", "no duplicate keys (rows with a null key are skipped)"),
34
+ "col_exists": ("`columns`", "column present"),
35
+ }
36
+
37
+ EXAMPLE = """\
38
+ name: people
39
+ rows: 1000
40
+ seed: 42
41
+ locale: IT
42
+ output:
43
+ format: csv
44
+ path: people.csv
45
+ columns:
46
+ id:
47
+ type: int
48
+ unique: true
49
+ min: 1
50
+ max: 99999
51
+ name:
52
+ type: string
53
+ preset: name
54
+ email:
55
+ type: string
56
+ preset: email
57
+ age:
58
+ type: int
59
+ min: 18
60
+ max: 90
61
+ null_probability: 0.05
62
+ score:
63
+ type: float
64
+ min: 0
65
+ max: 100
66
+ precision: 1
67
+ status:
68
+ type: string
69
+ values: [active, inactive, pending]
70
+ validation:
71
+ - rule: col_vals_not_null
72
+ columns: [id, name, email]
73
+ - rule: col_vals_between
74
+ column: age
75
+ min: 18
76
+ max: 90
77
+ - rule: col_vals_in_set
78
+ column: status
79
+ values: [active, inactive, pending]
80
+ - rule: rows_distinct
81
+ columns: [id]
82
+ """
83
+
84
+
85
+ def guide() -> str:
86
+ types = "\n".join(f"| `{t}` | {TYPE_OPTIONS.get(t, '-')} |" for t in sorted(VALID_TYPES))
87
+ rules = "\n".join(
88
+ f"| `{r}` | {RULE_PARAMS.get(r, ('-', '-'))[0]} | {RULE_PARAMS.get(r, ('-', '-'))[1]} |"
89
+ for r in sorted(VALID_RULES)
90
+ )
91
+ presets = ", ".join(f"`{p}`" for p in sorted(STRING_PRESETS))
92
+ return f"""# fauxdata {__version__} - guide for AI agents
93
+
94
+ fauxdata generates realistic fake datasets from a YAML schema and validates datasets against the same schema.
95
+
96
+ ## Workflow
97
+
98
+ 1. Write a schema (see the example below), or infer one from a real table:
99
+ `fauxdata infer real.csv --out schema.yml --json`
100
+ 2. Generate data and validate it in one step:
101
+ `fauxdata generate schema.yml --out data.csv --validate --json`
102
+ 3. Validate any existing file against a schema:
103
+ `fauxdata validate data.csv schema.yml --json`
104
+ 4. Inspect a file (column stats and first rows):
105
+ `fauxdata preview data.csv --json`
106
+
107
+ ## Machine-readable output
108
+
109
+ - With `--json`, stdout contains exactly one JSON object and nothing else.
110
+ - On error: `{{"error": "..."}}` on stdout, exit code 1.
111
+ - `validate --json`: `{{"passed": bool, "rows": int, "results": [{{"step", "rule", "column", "passed", "failed", "total", "ok", "evaluated"}}]}}`
112
+ - `generate --json`: `{{"output_path", "format", "rows", "seed", "dry_run", "validation": null | {{"passed", "results"}}}}`
113
+ - `infer --json`: `{{"schema_path", "columns": [names], "validation_rules": int}}`
114
+ - `preview --json`: `{{"rows", "columns": [{{"name", "type", "nulls", "unique", "min", "max"}}], "head": [rows]}}`
115
+ - `evaluated: false` means the step could not run (e.g. its column is missing from the data); such a step has `ok: false`.
116
+ - `--json` cannot be combined with `--out -` (that mode writes data or YAML to stdout).
117
+
118
+ ## Exit codes
119
+
120
+ - `0`: success, all validation rules passed
121
+ - `1`: error, or at least one validation rule failed
122
+
123
+ ## Schema
124
+
125
+ Top-level keys: `name` (required), `columns` (required), `rows`, `seed`, `locale` (ISO country code, e.g. `US`, `IT`, `DE`, `JP`), `description`, `output.format` (`csv`, `parquet`, `json`, `jsonl`), `output.path`, `validation`.
126
+
127
+ Every column needs `type`. Any column also accepts `nullable` and `null_probability` (0-1).
128
+
129
+ | Type | Options |
130
+ |------|---------|
131
+ {types}
132
+
133
+ Notes:
134
+ - `precision` rounds floats; bounds with more decimals are moved inward so values never leave `min`/`max`.
135
+ - `values` restricts a column to a fixed set (strings, ints or floats).
136
+ - `pattern` generates strings matching a regex.
137
+
138
+ ### String presets
139
+
140
+ {presets}
141
+
142
+ Presets are locale-aware and coherent within a row (the email matches the name, the city matches the country). Known limit: `unique: true` is not enforced on person and address presets such as `name`, `email`, `user_name`, `city` (pointblank issue #441). Validate with `rows_distinct` if uniqueness matters.
143
+
144
+ ## Validation rules
145
+
146
+ | Rule | Parameters | Checks |
147
+ |------|------------|--------|
148
+ {rules}
149
+
150
+ Value rules ignore nulls: use `col_vals_not_null` to forbid them.
151
+
152
+ ## Example schema
153
+
154
+ ```yaml
155
+ {EXAMPLE}```
156
+ """
157
+
158
+
159
+ def run():
160
+ sys.stdout.write(guide())
@@ -11,6 +11,7 @@ from rich.console import Console
11
11
  from rich.panel import Panel
12
12
  from rich.table import Table
13
13
 
14
+ from fauxdata import jsonmode
14
15
  from fauxdata.generator import generate_dataset
15
16
  from fauxdata.output import default_output_path, export_dataset, write_stdout
16
17
  from fauxdata.schema import load_schema
@@ -19,11 +20,13 @@ from fauxdata.validator import validate_dataset
19
20
  console = Console()
20
21
 
21
22
 
22
- def _generate(schema, n, rng_seed):
23
+ def _generate(schema, n, rng_seed, json_out=False):
23
24
  """Generate, translating generation errors into a clean CLI exit."""
24
25
  try:
25
26
  return generate_dataset(schema, rows=n, seed=rng_seed)
26
27
  except ValueError as e:
28
+ if json_out:
29
+ jsonmode.fail(str(e))
27
30
  rprint(f"[red]Error:[/red] {e}")
28
31
  raise typer.Exit(code=1)
29
32
 
@@ -36,6 +39,7 @@ def run(
36
39
  seed: Optional[int] = None,
37
40
  validate: bool = False,
38
41
  dry_run: bool = False,
42
+ json_out: bool = False,
39
43
  ):
40
44
  """Generate a fake dataset from a YAML schema."""
41
45
  schema = load_schema(schema_path)
@@ -47,6 +51,10 @@ def run(
47
51
 
48
52
  stdout_mode = output_path == "-"
49
53
 
54
+ if json_out:
55
+ _run_json(schema, n, rng_seed, output_fmt, output_path, validate, dry_run)
56
+ return
57
+
50
58
  if dry_run:
51
59
  rprint(Panel(f"[bold cyan]fauxdata generate --dry-run[/bold cyan] [dim]{schema_path}[/dim]", expand=False))
52
60
  rprint("[yellow]Dry run — no files will be written.[/yellow]\n")
@@ -81,6 +89,25 @@ def run(
81
89
  _run_validation(df, schema)
82
90
 
83
91
 
92
+ def _run_json(schema, n, rng_seed, output_fmt, output_path, validate, dry_run):
93
+ if output_path == "-":
94
+ jsonmode.fail("--json cannot be combined with --out - (the data would go to stdout)")
95
+ summary = {"output_path": output_path, "format": output_fmt, "rows": n, "seed": rng_seed,
96
+ "dry_run": dry_run, "validation": None}
97
+ if dry_run:
98
+ jsonmode.emit(summary)
99
+ return
100
+ df = _generate(schema, n, rng_seed, json_out=True)
101
+ summary["output_path"] = str(export_dataset(df, output_path, output_fmt))
102
+ passed = True
103
+ if validate:
104
+ passed, results = validate_dataset(df, schema)
105
+ summary["validation"] = {"passed": passed, "results": results}
106
+ jsonmode.emit(summary)
107
+ if not passed:
108
+ raise typer.Exit(code=1)
109
+
110
+
84
111
  def _print_schema_table(schema, n: int, seed):
85
112
  t = Table(title=f"Schema: {schema.name}", show_header=True, header_style="bold magenta")
86
113
  t.add_column("Column", style="cyan")
@@ -132,7 +159,12 @@ def _run_validation(df, schema):
132
159
  t.add_column("Status")
133
160
 
134
161
  for r in results:
135
- status = "[green]PASS[/green]" if r["ok"] else "[red]FAIL[/red]"
162
+ if r["ok"]:
163
+ status = "[green]PASS[/green]"
164
+ elif not r["evaluated"]:
165
+ status = "[red]NOT RUN[/red]"
166
+ else:
167
+ status = "[red]FAIL[/red]"
136
168
  t.add_row(
137
169
  str(r["step"]),
138
170
  r["rule"],