fauxdata-cli 0.1.6__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.
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/LOG.md +21 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/PKG-INFO +50 -4
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/README.md +47 -1
- fauxdata_cli-0.2.0/RELEASING.md +83 -0
- fauxdata_cli-0.2.0/docs/future-ideas.md +8 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/docs/index.html +12 -4
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/pyproject.toml +2 -2
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/__init__.py +1 -1
- fauxdata_cli-0.2.0/src/fauxdata/commands/agent.py +160 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/generate.py +34 -2
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/infer.py +37 -9
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/preview.py +38 -24
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/validate.py +22 -6
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/generator.py +24 -11
- fauxdata_cli-0.2.0/src/fauxdata/jsonmode.py +19 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/main.py +34 -6
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/schema.py +1 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/validator.py +28 -18
- fauxdata_cli-0.2.0/tasks/todo.md +90 -0
- fauxdata_cli-0.2.0/tests/test_agent.py +163 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_cli.py +13 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_generator.py +31 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_infer.py +51 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_schema.py +11 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_validator.py +67 -1
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/uv.lock +446 -8
- fauxdata_cli-0.1.6/.claude/settings.local.json +0 -12
- fauxdata_cli-0.1.6/share.png +0 -0
- fauxdata_cli-0.1.6/tasks/todo.md +0 -52
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/.gitignore +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/.python-version +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/LICENSE +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/docs/deployment.md +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/docs/share.png +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/schemas/events.yml +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/schemas/orders.yml +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/schemas/people.yml +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/__init__.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/commands/init.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/src/fauxdata/output.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/__init__.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/conftest.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_new_fields.py +0 -0
- {fauxdata_cli-0.1.6 → fauxdata_cli-0.2.0}/tests/test_output.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Log
|
|
2
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
|
+
|
|
3
24
|
## 2026-07-04 — fix issue #2: `precision` float ignorato
|
|
4
25
|
|
|
5
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: fauxdata-cli
|
|
3
|
-
Version: 0.
|
|
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.
|
|
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
|
|
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">
|
|
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.
|
|
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.
|
|
16
|
+
"pointblank>=1.0.1",
|
|
17
17
|
"typer>=0.12",
|
|
18
18
|
"rich>=13",
|
|
19
19
|
"pyfiglet>=1.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
|
-
|
|
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"],
|