fauxdata-cli 0.1.4__tar.gz → 0.1.6__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.4 → fauxdata_cli-0.1.6}/.claude/settings.local.json +3 -1
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/.gitignore +1 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/LOG.md +16 -1
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/PKG-INFO +26 -3
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/README.md +24 -1
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/pyproject.toml +2 -2
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/__init__.py +1 -1
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/commands/generate.py +11 -2
- fauxdata_cli-0.1.6/src/fauxdata/commands/infer.py +178 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/generator.py +27 -2
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/main.py +29 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/schema.py +11 -0
- fauxdata_cli-0.1.6/tasks/todo.md +52 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_generator.py +8 -0
- fauxdata_cli-0.1.6/tests/test_infer.py +171 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_new_fields.py +37 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/uv.lock +188 -9
- fauxdata_cli-0.1.4/.coverage +0 -0
- fauxdata_cli-0.1.4/tasks/todo.md +0 -38
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/.python-version +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/LICENSE +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/docs/deployment.md +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/docs/index.html +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/docs/share.png +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/schemas/events.yml +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/schemas/orders.yml +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/schemas/people.yml +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/share.png +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/commands/__init__.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/commands/init.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/commands/preview.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/commands/validate.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/output.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/src/fauxdata/validator.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/__init__.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/conftest.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_cli.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_output.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_schema.py +0 -0
- {fauxdata_cli-0.1.4 → fauxdata_cli-0.1.6}/tests/test_validator.py +0 -0
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
"WebFetch(domain:posit-dev.github.io)",
|
|
5
5
|
"WebFetch(domain:posit.co)",
|
|
6
6
|
"mcp__chrome-devtools__navigate_page",
|
|
7
|
-
"mcp__chrome-devtools__lighthouse_audit"
|
|
7
|
+
"mcp__chrome-devtools__lighthouse_audit",
|
|
8
|
+
"WebFetch(domain:github.com)",
|
|
9
|
+
"Bash(gh issue *)"
|
|
8
10
|
]
|
|
9
11
|
}
|
|
10
12
|
}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# Log
|
|
2
2
|
|
|
3
|
-
## 2026-04
|
|
3
|
+
## 2026-07-04 — fix issue #2: `precision` float ignorato
|
|
4
|
+
|
|
5
|
+
- 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.
|
|
6
|
+
- Fix in `generator.py`: dopo `pb.generate_dataset`, arrotondo con `pl.col(...).round(precision)` le colonne `float` con `precision` impostato.
|
|
7
|
+
- Verifica: reproducer della issue → output atteso (`27.74, 169.64, …`). Test `test_generate_float_precision`. 103/103 pass.
|
|
8
|
+
|
|
9
|
+
## 2026-06-12 — pointblank 0.25 + comando `infer`
|
|
10
|
+
|
|
11
|
+
- Bump `pointblank>=0.25` (uscito su PyPI). Schemi inclusi (people/orders/events) verificati: tutti PASS.
|
|
12
|
+
- Fase B regressioni: `rows_distinct` ora include righe con null (correttezza migliorata), lazy frame ok. fauxdata passa sempre DataFrame eager → nessun impatto.
|
|
13
|
+
- 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`.
|
|
14
|
+
- `ColumnSchema`: nuovi campi `min_length`/`max_length` per stringhe (round-trip fedele); passati a `pb.string_field`. Validazione parsing (negativi, min>max).
|
|
15
|
+
- infer CSV: `try_parse_dates=True` → date/datetime tipizzati invece che congelati come categorici.
|
|
16
|
+
- generate: errore chiaro su conflitto unique+range stretto ("too narrow a range") invece del traceback criptico di pointblank.
|
|
17
|
+
- Limite noto: colonne a bassa cardinalità congelate ai valori reali (possibile leak su colonne sensibili) — documentato nel README.
|
|
18
|
+
- Test: +14 (`tests/test_infer.py` + length in `test_new_fields.py`). 102/102 pass, coverage 83.2%.
|
|
4
19
|
|
|
5
20
|
- `init`: aggiunto `--description`, `--rows`, `--format`, `--yes`; questionary diventa fallback
|
|
6
21
|
- Tutti i comandi: `epilog` con esempi in `--help`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fauxdata-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
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>=0.25
|
|
17
17
|
Requires-Dist: polars>=1.0
|
|
18
18
|
Requires-Dist: pyfiglet>=1.0
|
|
19
19
|
Requires-Dist: pyyaml>=6.0
|
|
@@ -171,7 +171,7 @@ validation:
|
|
|
171
171
|
|------|-------------|---------|
|
|
172
172
|
| `int` | Integer | `min`, `max`, `unique` |
|
|
173
173
|
| `float` | Floating point | `min`, `max` |
|
|
174
|
-
| `string` | Text | `preset`, `values`, `unique` |
|
|
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) |
|
|
@@ -259,6 +259,29 @@ fauxdata generate schemas/people.yml --out - --format jsonl | wc -l
|
|
|
259
259
|
|
|
260
260
|
When `--out -` is used, all output messages are suppressed and only data is written to stdout.
|
|
261
261
|
|
|
262
|
+
### `fauxdata infer DATASET`
|
|
263
|
+
|
|
264
|
+
Build a YAML schema from a **real** dataset. fauxdata inspects the table (via pointblank's schema inference) and writes a schema with inferred ranges, categorical value sets, string presets (email, url…), uniqueness, null rates, and string lengths — plus matching `validation:` rules. Feed the result back to `fauxdata generate` to produce synthetic data that mirrors the real shape, without ever shipping the real values.
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
fauxdata infer real_data.csv # -> real_data.yml
|
|
268
|
+
fauxdata infer real_data.parquet --out schema.yml --name people
|
|
269
|
+
fauxdata infer real_data.csv --categorical-threshold 50 --no-detect-presets
|
|
270
|
+
fauxdata infer big.parquet --sample-size 10000 --out - | head # stream to stdout
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
| Option | Short | Default | Description |
|
|
274
|
+
|--------|-------|---------|-------------|
|
|
275
|
+
| `--out` | `-o` | `<name>.yml` | Output YAML path — use `-` for stdout |
|
|
276
|
+
| `--name` | `-n` | dataset stem | Schema name |
|
|
277
|
+
| `--rows` | `-r` | source rows | Rows to generate (written into the schema) |
|
|
278
|
+
| `--format` | `-f` | `csv` | Default output format written into the schema |
|
|
279
|
+
| `--categorical-threshold` | | `20` | Max unique values (int) or fraction (`0`–`1`) to treat a column as categorical |
|
|
280
|
+
| `--detect-presets` / `--no-detect-presets` | | on | Match string columns to known presets (email, url…) |
|
|
281
|
+
| `--sample-size` | | all rows | Sample N rows before analysis (for very large tables) |
|
|
282
|
+
|
|
283
|
+
> **Notes.** Low-cardinality columns (≤ `--categorical-threshold` distinct values) are frozen to their exact source values via `values:` — desirable for categories like `status`, but it means sensitive low-cardinality columns are reproduced verbatim. Raise the threshold or edit the schema if that's not what you want. Also, a column inferred as `unique` over a narrow integer range cannot generate many more rows than the source; widen `min`/`max` or drop `unique` to amplify.
|
|
284
|
+
|
|
262
285
|
### `fauxdata validate DATASET SCHEMA`
|
|
263
286
|
|
|
264
287
|
```
|
|
@@ -147,7 +147,7 @@ validation:
|
|
|
147
147
|
|------|-------------|---------|
|
|
148
148
|
| `int` | Integer | `min`, `max`, `unique` |
|
|
149
149
|
| `float` | Floating point | `min`, `max` |
|
|
150
|
-
| `string` | Text | `preset`, `values`, `unique` |
|
|
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) |
|
|
@@ -235,6 +235,29 @@ fauxdata generate schemas/people.yml --out - --format jsonl | wc -l
|
|
|
235
235
|
|
|
236
236
|
When `--out -` is used, all output messages are suppressed and only data is written to stdout.
|
|
237
237
|
|
|
238
|
+
### `fauxdata infer DATASET`
|
|
239
|
+
|
|
240
|
+
Build a YAML schema from a **real** dataset. fauxdata inspects the table (via pointblank's schema inference) and writes a schema with inferred ranges, categorical value sets, string presets (email, url…), uniqueness, null rates, and string lengths — plus matching `validation:` rules. Feed the result back to `fauxdata generate` to produce synthetic data that mirrors the real shape, without ever shipping the real values.
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
fauxdata infer real_data.csv # -> real_data.yml
|
|
244
|
+
fauxdata infer real_data.parquet --out schema.yml --name people
|
|
245
|
+
fauxdata infer real_data.csv --categorical-threshold 50 --no-detect-presets
|
|
246
|
+
fauxdata infer big.parquet --sample-size 10000 --out - | head # stream to stdout
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
| Option | Short | Default | Description |
|
|
250
|
+
|--------|-------|---------|-------------|
|
|
251
|
+
| `--out` | `-o` | `<name>.yml` | Output YAML path — use `-` for stdout |
|
|
252
|
+
| `--name` | `-n` | dataset stem | Schema name |
|
|
253
|
+
| `--rows` | `-r` | source rows | Rows to generate (written into the schema) |
|
|
254
|
+
| `--format` | `-f` | `csv` | Default output format written into the schema |
|
|
255
|
+
| `--categorical-threshold` | | `20` | Max unique values (int) or fraction (`0`–`1`) to treat a column as categorical |
|
|
256
|
+
| `--detect-presets` / `--no-detect-presets` | | on | Match string columns to known presets (email, url…) |
|
|
257
|
+
| `--sample-size` | | all rows | Sample N rows before analysis (for very large tables) |
|
|
258
|
+
|
|
259
|
+
> **Notes.** Low-cardinality columns (≤ `--categorical-threshold` distinct values) are frozen to their exact source values via `values:` — desirable for categories like `status`, but it means sensitive low-cardinality columns are reproduced verbatim. Raise the threshold or edit the schema if that's not what you want. Also, a column inferred as `unique` over a narrow integer range cannot generate many more rows than the source; widen `min`/`max` or drop `unique` to amplify.
|
|
260
|
+
|
|
238
261
|
### `fauxdata validate DATASET SCHEMA`
|
|
239
262
|
|
|
240
263
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fauxdata-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.6"
|
|
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>=0.25",
|
|
17
17
|
"typer>=0.12",
|
|
18
18
|
"rich>=13",
|
|
19
19
|
"pyfiglet>=1.0",
|
|
@@ -19,6 +19,15 @@ from fauxdata.validator import validate_dataset
|
|
|
19
19
|
console = Console()
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def _generate(schema, n, rng_seed):
|
|
23
|
+
"""Generate, translating generation errors into a clean CLI exit."""
|
|
24
|
+
try:
|
|
25
|
+
return generate_dataset(schema, rows=n, seed=rng_seed)
|
|
26
|
+
except ValueError as e:
|
|
27
|
+
rprint(f"[red]Error:[/red] {e}")
|
|
28
|
+
raise typer.Exit(code=1)
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
def run(
|
|
23
32
|
schema_path: str,
|
|
24
33
|
rows: Optional[int] = None,
|
|
@@ -54,12 +63,12 @@ def run(
|
|
|
54
63
|
rprint(Panel(f"[bold cyan]fauxdata generate[/bold cyan] [dim]{schema_path}[/dim]", expand=False))
|
|
55
64
|
|
|
56
65
|
if stdout_mode:
|
|
57
|
-
df =
|
|
66
|
+
df = _generate(schema, n, rng_seed)
|
|
58
67
|
write_stdout(df, output_fmt)
|
|
59
68
|
return
|
|
60
69
|
|
|
61
70
|
with console.status(f"[bold green]Generating {n} rows...[/bold green]"):
|
|
62
|
-
df =
|
|
71
|
+
df = _generate(schema, n, rng_seed)
|
|
63
72
|
|
|
64
73
|
_print_schema_table(schema, n, rng_seed)
|
|
65
74
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""fauxdata infer command - build a YAML schema from a real dataset.
|
|
2
|
+
|
|
3
|
+
Uses pointblank's ``schema_from_tbl`` (>=0.25) to infer rich constraints
|
|
4
|
+
(min/max, unique, null rate, allowed values, presets, string lengths) from an
|
|
5
|
+
existing table, then serializes them into a fauxdata YAML schema that can be
|
|
6
|
+
fed back to ``fauxdata generate``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import pointblank as pb
|
|
14
|
+
import polars as pl
|
|
15
|
+
import typer
|
|
16
|
+
import yaml
|
|
17
|
+
from rich import print as rprint
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
|
|
20
|
+
# Polars dtype name (str) -> fauxdata col_type
|
|
21
|
+
_DTYPE_MAP = {
|
|
22
|
+
"Int": "int",
|
|
23
|
+
"UInt": "int",
|
|
24
|
+
"Float": "float",
|
|
25
|
+
"String": "string",
|
|
26
|
+
"Utf8": "string",
|
|
27
|
+
"Boolean": "bool",
|
|
28
|
+
"Datetime": "datetime",
|
|
29
|
+
"Date": "date",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _col_type_from_dtype(dtype: str) -> str:
|
|
34
|
+
"""Map a pointblank/polars dtype name to a fauxdata col_type."""
|
|
35
|
+
for prefix, ctype in _DTYPE_MAP.items():
|
|
36
|
+
if dtype.startswith(prefix):
|
|
37
|
+
return ctype
|
|
38
|
+
return "string"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _field_to_spec(field) -> dict:
|
|
42
|
+
"""Convert an inferred pointblank Field into a fauxdata column spec dict."""
|
|
43
|
+
attrs = vars(field)
|
|
44
|
+
col_type = _col_type_from_dtype(attrs.get("dtype", "String"))
|
|
45
|
+
spec: dict = {"type": col_type}
|
|
46
|
+
|
|
47
|
+
if attrs.get("unique"):
|
|
48
|
+
spec["unique"] = True
|
|
49
|
+
if attrs.get("nullable"):
|
|
50
|
+
spec["nullable"] = True
|
|
51
|
+
np = attrs.get("null_probability")
|
|
52
|
+
if np:
|
|
53
|
+
spec["null_probability"] = round(float(np), 4)
|
|
54
|
+
|
|
55
|
+
if col_type in ("int", "float"):
|
|
56
|
+
if attrs.get("min_val") is not None:
|
|
57
|
+
spec["min"] = attrs["min_val"]
|
|
58
|
+
if attrs.get("max_val") is not None:
|
|
59
|
+
spec["max"] = attrs["max_val"]
|
|
60
|
+
if attrs.get("allowed"):
|
|
61
|
+
spec["values"] = list(attrs["allowed"])
|
|
62
|
+
|
|
63
|
+
elif col_type in ("date", "datetime"):
|
|
64
|
+
if attrs.get("min_date") is not None:
|
|
65
|
+
spec["min"] = str(attrs["min_date"])
|
|
66
|
+
if attrs.get("max_date") is not None:
|
|
67
|
+
spec["max"] = str(attrs["max_date"])
|
|
68
|
+
|
|
69
|
+
elif col_type == "string":
|
|
70
|
+
if attrs.get("preset"):
|
|
71
|
+
spec["preset"] = attrs["preset"]
|
|
72
|
+
elif attrs.get("allowed"):
|
|
73
|
+
spec["values"] = list(attrs["allowed"])
|
|
74
|
+
elif attrs.get("pattern"):
|
|
75
|
+
spec["pattern"] = attrs["pattern"]
|
|
76
|
+
if attrs.get("min_length") is not None:
|
|
77
|
+
spec["min_length"] = attrs["min_length"]
|
|
78
|
+
if attrs.get("max_length") is not None:
|
|
79
|
+
spec["max_length"] = attrs["max_length"]
|
|
80
|
+
|
|
81
|
+
return spec
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _build_validation(columns: list[tuple[str, dict]]) -> list[dict]:
|
|
85
|
+
"""Derive validation rules from the inferred column specs."""
|
|
86
|
+
rules: list[dict] = []
|
|
87
|
+
|
|
88
|
+
not_null = [name for name, spec in columns if not spec.get("nullable")]
|
|
89
|
+
if not_null:
|
|
90
|
+
rules.append({"rule": "col_vals_not_null", "columns": not_null})
|
|
91
|
+
|
|
92
|
+
for name, spec in columns:
|
|
93
|
+
if spec["type"] in ("int", "float") and "min" in spec and "max" in spec:
|
|
94
|
+
rules.append({"rule": "col_vals_between", "column": name,
|
|
95
|
+
"min": spec["min"], "max": spec["max"]})
|
|
96
|
+
|
|
97
|
+
for name, spec in columns:
|
|
98
|
+
if "values" in spec:
|
|
99
|
+
rules.append({"rule": "col_vals_in_set", "column": name, "values": list(spec["values"])})
|
|
100
|
+
|
|
101
|
+
unique_cols = [name for name, spec in columns if spec.get("unique")]
|
|
102
|
+
if unique_cols:
|
|
103
|
+
rules.append({"rule": "rows_distinct", "columns": unique_cols})
|
|
104
|
+
|
|
105
|
+
return rules
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _load_table(path: str) -> pl.DataFrame:
|
|
109
|
+
ext = path.rsplit(".", 1)[-1].lower()
|
|
110
|
+
if ext == "csv":
|
|
111
|
+
# try_parse_dates so date/datetime columns are typed (not frozen as categorical strings)
|
|
112
|
+
return pl.read_csv(path, try_parse_dates=True)
|
|
113
|
+
if ext == "parquet":
|
|
114
|
+
return pl.read_parquet(path)
|
|
115
|
+
if ext == "json":
|
|
116
|
+
return pl.read_json(path)
|
|
117
|
+
if ext in ("jsonl", "ndjson"):
|
|
118
|
+
return pl.read_ndjson(path)
|
|
119
|
+
rprint(f"[red]Unsupported file format: .{ext}[/red]")
|
|
120
|
+
raise typer.Exit(code=1)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def run(
|
|
124
|
+
dataset_path: str,
|
|
125
|
+
out: str | None = None,
|
|
126
|
+
name: str | None = None,
|
|
127
|
+
rows: int | None = None,
|
|
128
|
+
fmt: str = "csv",
|
|
129
|
+
categorical_threshold: float = 20,
|
|
130
|
+
detect_presets: bool = True,
|
|
131
|
+
sample_size: int | None = None,
|
|
132
|
+
):
|
|
133
|
+
"""Infer a fauxdata YAML schema from a real dataset."""
|
|
134
|
+
rprint(Panel(
|
|
135
|
+
f"[bold cyan]fauxdata infer[/bold cyan] [dim]{dataset_path}[/dim]",
|
|
136
|
+
expand=False,
|
|
137
|
+
))
|
|
138
|
+
|
|
139
|
+
df = _load_table(dataset_path)
|
|
140
|
+
rprint(f" Loaded [bold]{len(df)}[/bold] rows, [bold]{len(df.columns)}[/bold] columns")
|
|
141
|
+
|
|
142
|
+
pb_schema = pb.schema_from_tbl(
|
|
143
|
+
df,
|
|
144
|
+
categorical_threshold=categorical_threshold,
|
|
145
|
+
detect_presets=detect_presets,
|
|
146
|
+
sample_size=sample_size,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
columns = [(col_name, _field_to_spec(field)) for col_name, field in pb_schema.columns]
|
|
150
|
+
|
|
151
|
+
schema_name = name or Path(dataset_path).stem
|
|
152
|
+
out_rows = rows if rows is not None else len(df)
|
|
153
|
+
|
|
154
|
+
schema_dict = {
|
|
155
|
+
"name": schema_name,
|
|
156
|
+
"description": f"Inferred from {Path(dataset_path).name}",
|
|
157
|
+
"rows": out_rows,
|
|
158
|
+
"seed": 42,
|
|
159
|
+
"output": {"format": fmt, "path": f"{schema_name}.{fmt}"},
|
|
160
|
+
"columns": {col_name: spec for col_name, spec in columns},
|
|
161
|
+
"validation": _build_validation(columns),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
text = yaml.safe_dump(schema_dict, sort_keys=False, allow_unicode=True, default_flow_style=False)
|
|
165
|
+
|
|
166
|
+
out_path = out or f"{schema_name}.yml"
|
|
167
|
+
if out_path == "-":
|
|
168
|
+
import sys
|
|
169
|
+
sys.stdout.write(text)
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
Path(out_path).write_text(text)
|
|
173
|
+
rprint(f"[green]Created[/green] [bold]{out_path}[/bold] "
|
|
174
|
+
f"([bold]{len(columns)}[/bold] columns, "
|
|
175
|
+
f"[bold]{len(schema_dict['validation'])}[/bold] validation rules)")
|
|
176
|
+
rprint("[dim]Generate synthetic data with:[/dim]")
|
|
177
|
+
rprint(f" [cyan]fauxdata generate {out_path} --validate[/cyan]")
|
|
178
|
+
rprint(f"schema_path: {out_path}")
|
|
@@ -16,7 +16,27 @@ def generate_dataset(schema: SchemaConfig, rows: int | None = None, seed: int |
|
|
|
16
16
|
pb_schema = _build_pb_schema(schema)
|
|
17
17
|
country = schema.locale or "US"
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
try:
|
|
20
|
+
df = pb.generate_dataset(pb_schema, n=n, seed=rng_seed, country=country)
|
|
21
|
+
except ValueError as e:
|
|
22
|
+
if "unique values" in str(e):
|
|
23
|
+
unique_cols = [c.name for c in schema.columns if c.unique]
|
|
24
|
+
raise ValueError(
|
|
25
|
+
f"Cannot generate {n} rows: a unique column has too narrow a range "
|
|
26
|
+
f"(unique columns: {', '.join(unique_cols) or 'n/a'}). "
|
|
27
|
+
f"Reduce --rows, widen min/max, or drop 'unique'."
|
|
28
|
+
) from e
|
|
29
|
+
raise
|
|
30
|
+
|
|
31
|
+
# pointblank has no float precision option: round after generation
|
|
32
|
+
round_exprs = [
|
|
33
|
+
pl.col(col.name).round(col.precision)
|
|
34
|
+
for col in schema.columns
|
|
35
|
+
if col.col_type == "float" and col.precision is not None
|
|
36
|
+
]
|
|
37
|
+
if round_exprs:
|
|
38
|
+
df = df.with_columns(round_exprs)
|
|
39
|
+
|
|
20
40
|
return df
|
|
21
41
|
|
|
22
42
|
|
|
@@ -82,7 +102,12 @@ def _col_to_field(col: ColumnSchema):
|
|
|
82
102
|
elif col.preset:
|
|
83
103
|
return pb.string_field(preset=col.preset, nullable=nullable, unique=unique, **np_kwargs)
|
|
84
104
|
else:
|
|
85
|
-
|
|
105
|
+
len_kwargs = {}
|
|
106
|
+
if col.min_length is not None:
|
|
107
|
+
len_kwargs["min_length"] = col.min_length
|
|
108
|
+
if col.max_length is not None:
|
|
109
|
+
len_kwargs["max_length"] = col.max_length
|
|
110
|
+
return pb.string_field(nullable=nullable, unique=unique, **len_kwargs, **np_kwargs)
|
|
86
111
|
|
|
87
112
|
else:
|
|
88
113
|
return pb.string_field(nullable=nullable, **np_kwargs)
|
|
@@ -65,6 +65,35 @@ def init_cmd(
|
|
|
65
65
|
run(name=name, description=description, rows=rows, fmt=fmt, yes=yes)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
@app.command(
|
|
69
|
+
"infer",
|
|
70
|
+
epilog=(
|
|
71
|
+
"Examples:\n\n"
|
|
72
|
+
" fauxdata infer real_data.csv\n\n"
|
|
73
|
+
" fauxdata infer real_data.parquet --out schema.yml --name people\n\n"
|
|
74
|
+
" fauxdata infer real_data.csv --categorical-threshold 50 --no-detect-presets\n\n"
|
|
75
|
+
" fauxdata infer big.parquet --sample-size 10000 --out - | head\n"
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
def infer_cmd(
|
|
79
|
+
dataset: str = typer.Argument(..., help="Path to a real dataset (csv, parquet, json, jsonl)"),
|
|
80
|
+
out: Optional[str] = typer.Option(None, "--out", "-o", help="Output YAML path (use - for stdout; default: <name>.yml)"),
|
|
81
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Schema name (default: dataset file stem)"),
|
|
82
|
+
rows: Optional[int] = typer.Option(None, "--rows", "-r", help="Rows to generate (default: rows in source table)"),
|
|
83
|
+
fmt: str = typer.Option("csv", "--format", "-f", help="Default output format in the schema: csv, parquet, json, jsonl"),
|
|
84
|
+
categorical_threshold: float = typer.Option(20, "--categorical-threshold", help="Max unique values (int) or fraction (0-1) to treat a column as categorical"),
|
|
85
|
+
detect_presets: bool = typer.Option(True, "--detect-presets/--no-detect-presets", help="Match string columns to known presets (email, url, ...)"),
|
|
86
|
+
sample_size: Optional[int] = typer.Option(None, "--sample-size", help="Sample N rows before analysis (for very large tables)"),
|
|
87
|
+
):
|
|
88
|
+
"""Infer a YAML schema from a real dataset (powered by pointblank schema inference)."""
|
|
89
|
+
from fauxdata.commands.infer import run
|
|
90
|
+
run(
|
|
91
|
+
dataset_path=dataset, out=out, name=name, rows=rows, fmt=fmt,
|
|
92
|
+
categorical_threshold=categorical_threshold, detect_presets=detect_presets,
|
|
93
|
+
sample_size=sample_size,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
68
97
|
@app.command(
|
|
69
98
|
"generate",
|
|
70
99
|
epilog=(
|
|
@@ -59,6 +59,8 @@ class ColumnSchema:
|
|
|
59
59
|
values: list | None = None # for in_set
|
|
60
60
|
pattern: str | None = None # regex pattern for string generation
|
|
61
61
|
null_probability: float | None = None # e.g. 0.1 = 10% nulls
|
|
62
|
+
min_length: int | None = None # min string length (string columns)
|
|
63
|
+
max_length: int | None = None # max string length (string columns)
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
@dataclass
|
|
@@ -148,6 +150,13 @@ def _parse_column(name: str, data: dict) -> ColumnSchema:
|
|
|
148
150
|
if null_probability is not None and not (0.0 <= float(null_probability) <= 1.0):
|
|
149
151
|
raise ValueError(f"Column '{name}': null_probability must be between 0.0 and 1.0")
|
|
150
152
|
|
|
153
|
+
min_length = data.get("min_length", None)
|
|
154
|
+
max_length = data.get("max_length", None)
|
|
155
|
+
if min_length is not None and int(min_length) < 0:
|
|
156
|
+
raise ValueError(f"Column '{name}': min_length must be >= 0")
|
|
157
|
+
if min_length is not None and max_length is not None and int(min_length) > int(max_length):
|
|
158
|
+
raise ValueError(f"Column '{name}': min_length must be <= max_length")
|
|
159
|
+
|
|
151
160
|
return ColumnSchema(
|
|
152
161
|
name=name,
|
|
153
162
|
col_type=col_type,
|
|
@@ -161,6 +170,8 @@ def _parse_column(name: str, data: dict) -> ColumnSchema:
|
|
|
161
170
|
values=data.get("values", None),
|
|
162
171
|
pattern=data.get("pattern", None),
|
|
163
172
|
null_probability=float(null_probability) if null_probability is not None else None,
|
|
173
|
+
min_length=int(min_length) if min_length is not None else None,
|
|
174
|
+
max_length=int(max_length) if max_length is not None else None,
|
|
164
175
|
)
|
|
165
176
|
|
|
166
177
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Impatto pointblank v0.25.0 su fauxdata
|
|
2
|
+
|
|
3
|
+
## Verdetto
|
|
4
|
+
Sì, impatto **forte**. La release introduce `schema_from_tbl()` / `Schema.from_table()`:
|
|
5
|
+
inferisce vincoli ricchi (min/max, unique, null rate, `allowed=`, preset come email/uuid)
|
|
6
|
+
da una tabella reale, pensati esplicitamente per la generazione sintetica.
|
|
7
|
+
È esattamente la premessa del progetto ("real fake datasets"): dato reale → schema → dato finto.
|
|
8
|
+
|
|
9
|
+
## BLOCCO — non installabile da PyPI
|
|
10
|
+
- v0.25.0 è **solo taggata su GitHub**, PyPI è fermo a 0.24.0.
|
|
11
|
+
- Non si può fare `pointblank>=0.25` + pubblicare fauxdata finché non esce su PyPI.
|
|
12
|
+
- API verificata sul tag git (non dal summary): firma confermata
|
|
13
|
+
`schema_from_tbl(tbl, *, infer_constraints=True, categorical_threshold=20, detect_presets=True, sample_size=None)`.
|
|
14
|
+
|
|
15
|
+
## Fase A — nuovo comando `fauxdata infer` (la feature)
|
|
16
|
+
Flusso: tabella reale (CSV/Parquet via polars) → `schema_from_tbl` → YAML fauxdata → `generate`.
|
|
17
|
+
- Nuovo `commands/infer.py`: legge file, chiama `schema_from_tbl`, serializza in YAML fauxdata.
|
|
18
|
+
- Convertitore Field→ColumnSchema: mapping `dtype` (Int64/String/Float64/...) → `col_type`,
|
|
19
|
+
`allowed`→`values`, `preset`, `min_val/max_val`→`min/max`, `unique`, `null_probability`.
|
|
20
|
+
- Flag da esporre: `--categorical-threshold`, `--no-detect-presets`, `--sample-size`, `--out schema.yml`.
|
|
21
|
+
- Mapping **lossy** da segnalare: `StringField.min_length/max_length` non hanno campo nel YAML fauxdata.
|
|
22
|
+
- Test di accettazione (vero criterio): infer da tabella campione → YAML → `generate` →
|
|
23
|
+
output rispetta gli stessi vincoli (range, set, preset). NON basta "ritorna uno Schema".
|
|
24
|
+
|
|
25
|
+
## Fase B — verifiche di regressione (cambi di comportamento)
|
|
26
|
+
- `rows_distinct()`: in 0.25 le righe con null **non** sono più escluse. fauxdata lo usa nei
|
|
27
|
+
`VALID_RULES` del validator → può cambiare pass/fail su schemi esistenti. Da testare.
|
|
28
|
+
- Lazy frame non più collezionati prematuramente: verificare che generator/validator non assuma DataFrame eager.
|
|
29
|
+
|
|
30
|
+
## Fuori scope (non rilevante per fauxdata)
|
|
31
|
+
- `Contract`/`Pipeline`, attachments multi-modali in `prompt()`, fix MCP server.
|
|
32
|
+
|
|
33
|
+
## Decisioni (prese 2026-06-12)
|
|
34
|
+
- **Timing**: solo piano. NON scrivere codice finché pointblank 0.25.0 non è su PyPI (ora è solo tag git, PyPI=0.24.0).
|
|
35
|
+
- **Validation**: `infer` genera sia `columns:` sia `validation:` (regole derivate dai vincoli — es. `col_vals_between` da min/max, `col_vals_in_set` da allowed, `col_vals_not_null`, `rows_distinct` per colonne unique).
|
|
36
|
+
- **Lunghezza stringhe**: aggiungere `min_length`/`max_length` a `ColumnSchema` per round-trip fedele
|
|
37
|
+
→ tocca `schema.py` (parse + dataclass), `generator.py` (`pb.string_field(min_length=, max_length=)`), test.
|
|
38
|
+
|
|
39
|
+
## Trigger di ripresa
|
|
40
|
+
Quando `pip index versions pointblank` (o PyPI) mostra 0.25.0:
|
|
41
|
+
1. Bump `pyproject.toml`: `pointblank>=0.25`, `uv sync`.
|
|
42
|
+
2. Fase B prima (regressioni rows_distinct / lazy frame) — sono rischi su codice esistente.
|
|
43
|
+
3. Poi Fase A (comando `infer` + estensione min_length/max_length).
|
|
44
|
+
|
|
45
|
+
## Review (completato 2026-06-12)
|
|
46
|
+
- **Fatto**: bump 0.25, comando `infer`, campi `min_length`/`max_length`, validation derivate, fix datetime CSV, errore chiaro unique+range. 102/102 test, coverage 83.2%.
|
|
47
|
+
- **Fase B esito**: nessuna modifica necessaria. Schemi inclusi tutti PASS; `rows_distinct`/lazy frame non impattano (fauxdata usa DataFrame eager).
|
|
48
|
+
- **Limiti noti documentati (README)**:
|
|
49
|
+
1. Bassa cardinalità → valori reali congelati in `values:` (leak su colonne sensibili). Mitigazione: `--categorical-threshold` / editing manuale.
|
|
50
|
+
2. `unique` + range int stretto non amplifica oltre la sorgente. Errore chiaro a runtime.
|
|
51
|
+
- **Non fatto (decisione utente)**: bump versione pacchetto (0.1.4 → 0.1.5) e publish PyPI — non richiesto, lasciato all'utente.
|
|
52
|
+
- **Possibili migliorie future** (docs/future-ideas.md candidate): opzione `infer` per non-categorizzare colonne testuali libere; flag per amplificazione (rilassa unique).
|
|
@@ -87,6 +87,14 @@ def test_generate_seed_reproducibility(minimal_schema):
|
|
|
87
87
|
assert df1.equals(df2)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def test_generate_float_precision():
|
|
91
|
+
"""float `precision` rounds generated values to the requested decimals."""
|
|
92
|
+
schema = _make_schema([ColumnSchema(name="f", col_type="float", min=1.0, max=200.0, precision=2)])
|
|
93
|
+
df = generate_dataset(schema, rows=20, seed=1)
|
|
94
|
+
for v in df["f"].to_list():
|
|
95
|
+
assert v == round(v, 2)
|
|
96
|
+
|
|
97
|
+
|
|
90
98
|
def test_generate_all_types():
|
|
91
99
|
"""Generate a dataset with all supported column types."""
|
|
92
100
|
schema = _make_schema([
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Tests for the infer command and schema inference (pointblank >=0.25)."""
|
|
2
|
+
|
|
3
|
+
import polars as pl
|
|
4
|
+
import pytest
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from fauxdata.commands.infer import (
|
|
8
|
+
_col_type_from_dtype,
|
|
9
|
+
_field_to_spec,
|
|
10
|
+
_build_validation,
|
|
11
|
+
)
|
|
12
|
+
from fauxdata.commands import infer as infer_mod
|
|
13
|
+
from fauxdata.schema import load_schema
|
|
14
|
+
from fauxdata.generator import generate_dataset
|
|
15
|
+
from fauxdata.validator import validate_dataset
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# --- dtype mapping ---
|
|
19
|
+
|
|
20
|
+
@pytest.mark.parametrize("dtype,expected", [
|
|
21
|
+
("Int64", "int"),
|
|
22
|
+
("UInt32", "int"),
|
|
23
|
+
("Float64", "float"),
|
|
24
|
+
("String", "string"),
|
|
25
|
+
("Utf8", "string"),
|
|
26
|
+
("Boolean", "bool"),
|
|
27
|
+
("Date", "date"),
|
|
28
|
+
("Datetime(time_unit='us')", "datetime"),
|
|
29
|
+
("SomethingWeird", "string"), # fallback
|
|
30
|
+
])
|
|
31
|
+
def test_col_type_from_dtype(dtype, expected):
|
|
32
|
+
assert _col_type_from_dtype(dtype) == expected
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# --- field -> spec ---
|
|
36
|
+
|
|
37
|
+
def _spec_of(df, col):
|
|
38
|
+
import pointblank as pb
|
|
39
|
+
s = pb.schema_from_tbl(df)
|
|
40
|
+
fields = dict(s.columns)
|
|
41
|
+
return _field_to_spec(fields[col])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_field_to_spec_int_range_unique():
|
|
45
|
+
df = pl.DataFrame({"id": [1, 2, 3, 4, 5]})
|
|
46
|
+
spec = _spec_of(df, "id")
|
|
47
|
+
assert spec["type"] == "int"
|
|
48
|
+
assert spec["min"] == 1
|
|
49
|
+
assert spec["max"] == 5
|
|
50
|
+
assert spec["unique"] is True
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_field_to_spec_email_preset():
|
|
54
|
+
df = pl.DataFrame({"email": [f"u{i}@ex.com" for i in range(6)]})
|
|
55
|
+
spec = _spec_of(df, "email")
|
|
56
|
+
assert spec["type"] == "string"
|
|
57
|
+
assert spec.get("preset") == "email"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_field_to_spec_categorical_values():
|
|
61
|
+
df = pl.DataFrame({"status": ["a", "b", "a", "b", "a", "b"]})
|
|
62
|
+
spec = _spec_of(df, "status")
|
|
63
|
+
assert spec["type"] == "string"
|
|
64
|
+
assert sorted(spec["values"]) == ["a", "b"]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# --- validation derivation ---
|
|
68
|
+
|
|
69
|
+
def test_build_validation_rules():
|
|
70
|
+
columns = [
|
|
71
|
+
("id", {"type": "int", "unique": True, "min": 1, "max": 9}),
|
|
72
|
+
("status", {"type": "string", "values": ["a", "b"]}),
|
|
73
|
+
("note", {"type": "string", "nullable": True}),
|
|
74
|
+
]
|
|
75
|
+
rules = _build_validation(columns)
|
|
76
|
+
kinds = [r["rule"] for r in rules]
|
|
77
|
+
assert "col_vals_not_null" in kinds
|
|
78
|
+
assert "col_vals_between" in kinds
|
|
79
|
+
assert "col_vals_in_set" in kinds
|
|
80
|
+
assert "rows_distinct" in kinds
|
|
81
|
+
# nullable column excluded from not_null
|
|
82
|
+
nn = next(r for r in rules if r["rule"] == "col_vals_not_null")
|
|
83
|
+
assert "note" not in nn["columns"]
|
|
84
|
+
assert "id" in nn["columns"]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_build_validation_in_set_is_copy():
|
|
88
|
+
"""values in the rule must not alias the column spec list (no YAML anchor)."""
|
|
89
|
+
vals = ["a", "b"]
|
|
90
|
+
columns = [("status", {"type": "string", "values": vals})]
|
|
91
|
+
rules = _build_validation(columns)
|
|
92
|
+
rule_vals = next(r for r in rules if r["rule"] == "col_vals_in_set")["values"]
|
|
93
|
+
assert rule_vals == vals
|
|
94
|
+
assert rule_vals is not vals
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# --- end-to-end round trip ---
|
|
98
|
+
|
|
99
|
+
def test_infer_roundtrip(tmp_path):
|
|
100
|
+
real = pl.DataFrame({
|
|
101
|
+
"id": list(range(1, 9)),
|
|
102
|
+
"email": [f"u{i}@ex.com" for i in range(8)],
|
|
103
|
+
"age": [20, 35, 40, 55, 60, 25, 33, 48],
|
|
104
|
+
"status": ["active", "inactive"] * 4,
|
|
105
|
+
"price": [1.5, 2.0, 3.25, 4.0, 5.5, 2.2, 3.3, 4.4],
|
|
106
|
+
})
|
|
107
|
+
src = tmp_path / "real.csv"
|
|
108
|
+
real.write_csv(src)
|
|
109
|
+
|
|
110
|
+
out = tmp_path / "inferred.yml"
|
|
111
|
+
infer_mod.run(dataset_path=str(src), out=str(out))
|
|
112
|
+
|
|
113
|
+
assert out.exists()
|
|
114
|
+
data = yaml.safe_load(out.read_text())
|
|
115
|
+
assert set(data["columns"]) == {"id", "email", "age", "status", "price"}
|
|
116
|
+
assert data["columns"]["email"]["preset"] == "email"
|
|
117
|
+
assert data["rows"] == 8
|
|
118
|
+
|
|
119
|
+
# the inferred schema generates valid data that passes its own rules.
|
|
120
|
+
# Use the schema's own row count (calibrated to the source size): inferred
|
|
121
|
+
# unique columns over a narrow integer range cannot yield many more rows.
|
|
122
|
+
schema = load_schema(out)
|
|
123
|
+
df = generate_dataset(schema)
|
|
124
|
+
all_passed, _ = validate_dataset(df, schema)
|
|
125
|
+
assert all_passed
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_infer_datetime_from_csv_typed(tmp_path):
|
|
129
|
+
"""date/datetime CSV columns are inferred as typed, not frozen as categorical strings."""
|
|
130
|
+
import datetime
|
|
131
|
+
real = pl.DataFrame({
|
|
132
|
+
"ts": [datetime.datetime(2020, 1, 1, 8, 30) + datetime.timedelta(days=i * 40) for i in range(8)],
|
|
133
|
+
})
|
|
134
|
+
src = tmp_path / "dt.csv"
|
|
135
|
+
real.write_csv(src)
|
|
136
|
+
out = tmp_path / "dt.yml"
|
|
137
|
+
infer_mod.run(dataset_path=str(src), out=str(out))
|
|
138
|
+
data = yaml.safe_load(out.read_text())
|
|
139
|
+
assert data["columns"]["ts"]["type"] == "datetime"
|
|
140
|
+
# round-trips through generation
|
|
141
|
+
schema = load_schema(out)
|
|
142
|
+
assert len(generate_dataset(schema)) == schema.rows
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_generate_unique_range_too_narrow_clear_error():
|
|
146
|
+
"""Amplifying a unique column beyond its range raises a clear, named error."""
|
|
147
|
+
from fauxdata.schema import SchemaConfig, ColumnSchema
|
|
148
|
+
schema = SchemaConfig(
|
|
149
|
+
name="x", rows=5, seed=1,
|
|
150
|
+
columns=[ColumnSchema(name="id", col_type="int", min=1, max=5, unique=True)],
|
|
151
|
+
)
|
|
152
|
+
with pytest.raises(ValueError, match="too narrow a range"):
|
|
153
|
+
generate_dataset(schema, rows=100)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_infer_unsupported_format(tmp_path):
|
|
157
|
+
import typer
|
|
158
|
+
bad = tmp_path / "data.xyz"
|
|
159
|
+
bad.write_text("nope")
|
|
160
|
+
with pytest.raises(typer.Exit):
|
|
161
|
+
infer_mod.run(dataset_path=str(bad))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_infer_stdout(tmp_path, capsys):
|
|
165
|
+
real = pl.DataFrame({"id": [1, 2, 3]})
|
|
166
|
+
src = tmp_path / "r.csv"
|
|
167
|
+
real.write_csv(src)
|
|
168
|
+
infer_mod.run(dataset_path=str(src), out="-")
|
|
169
|
+
captured = capsys.readouterr()
|
|
170
|
+
assert "columns:" in captured.out
|
|
171
|
+
assert "name: r" in captured.out
|
|
@@ -167,3 +167,40 @@ columns:
|
|
|
167
167
|
"--out", str(out), "--format", "csv"])
|
|
168
168
|
assert result.exit_code == 0, result.output
|
|
169
169
|
assert out.exists()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# --- min_length / max_length (string columns) ---
|
|
173
|
+
|
|
174
|
+
def test_min_max_length_parsing():
|
|
175
|
+
schema = _parse_schema({
|
|
176
|
+
"name": "x",
|
|
177
|
+
"columns": {"code": {"type": "string", "min_length": 4, "max_length": 8}},
|
|
178
|
+
})
|
|
179
|
+
col = schema.columns[0]
|
|
180
|
+
assert col.min_length == 4
|
|
181
|
+
assert col.max_length == 8
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_min_length_negative_rejected():
|
|
185
|
+
with pytest.raises(ValueError, match="min_length"):
|
|
186
|
+
_parse_schema({
|
|
187
|
+
"name": "x",
|
|
188
|
+
"columns": {"code": {"type": "string", "min_length": -1}},
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_min_length_gt_max_length_rejected():
|
|
193
|
+
with pytest.raises(ValueError, match="min_length"):
|
|
194
|
+
_parse_schema({
|
|
195
|
+
"name": "x",
|
|
196
|
+
"columns": {"code": {"type": "string", "min_length": 9, "max_length": 4}},
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_generate_respects_string_length():
|
|
201
|
+
schema = SchemaConfig(
|
|
202
|
+
name="x", rows=10, seed=1,
|
|
203
|
+
columns=[ColumnSchema(name="code", col_type="string", min_length=6, max_length=6)],
|
|
204
|
+
)
|
|
205
|
+
df = generate_dataset(schema)
|
|
206
|
+
assert df["code"].str.len_chars().to_list() == [6] * 10
|
|
@@ -29,6 +29,76 @@ wheels = [
|
|
|
29
29
|
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
|
|
30
30
|
]
|
|
31
31
|
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "cffi"
|
|
34
|
+
version = "2.0.0"
|
|
35
|
+
source = { registry = "https://pypi.org/simple" }
|
|
36
|
+
dependencies = [
|
|
37
|
+
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
|
38
|
+
]
|
|
39
|
+
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
|
40
|
+
wheels = [
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
|
|
43
|
+
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
|
|
44
|
+
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
|
|
45
|
+
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
|
|
46
|
+
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
|
|
47
|
+
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
|
|
48
|
+
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
|
|
49
|
+
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
|
|
52
|
+
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
|
|
68
|
+
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
|
|
69
|
+
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
|
100
|
+
]
|
|
101
|
+
|
|
32
102
|
[[package]]
|
|
33
103
|
name = "charset-normalizer"
|
|
34
104
|
version = "3.4.5"
|
|
@@ -250,7 +320,7 @@ wheels = [
|
|
|
250
320
|
|
|
251
321
|
[[package]]
|
|
252
322
|
name = "fauxdata-cli"
|
|
253
|
-
version = "0.1.
|
|
323
|
+
version = "0.1.6"
|
|
254
324
|
source = { editable = "." }
|
|
255
325
|
dependencies = [
|
|
256
326
|
{ name = "pointblank" },
|
|
@@ -270,7 +340,7 @@ dev = [
|
|
|
270
340
|
|
|
271
341
|
[package.metadata]
|
|
272
342
|
requires-dist = [
|
|
273
|
-
{ name = "pointblank", specifier = ">=0.
|
|
343
|
+
{ name = "pointblank", specifier = ">=0.25" },
|
|
274
344
|
{ name = "polars", specifier = ">=1.0" },
|
|
275
345
|
{ name = "pyfiglet", specifier = ">=1.0" },
|
|
276
346
|
{ name = "pyyaml", specifier = ">=6.0" },
|
|
@@ -287,20 +357,21 @@ dev = [
|
|
|
287
357
|
|
|
288
358
|
[[package]]
|
|
289
359
|
name = "great-tables"
|
|
290
|
-
version = "0.
|
|
360
|
+
version = "0.22.0"
|
|
291
361
|
source = { registry = "https://pypi.org/simple" }
|
|
292
362
|
dependencies = [
|
|
293
363
|
{ name = "babel" },
|
|
294
|
-
{ name = "commonmark" },
|
|
295
364
|
{ name = "faicons" },
|
|
296
365
|
{ name = "htmltools" },
|
|
297
366
|
{ name = "importlib-metadata" },
|
|
298
367
|
{ name = "importlib-resources" },
|
|
368
|
+
{ name = "multimark" },
|
|
369
|
+
{ name = "nokap" },
|
|
299
370
|
{ name = "typing-extensions" },
|
|
300
371
|
]
|
|
301
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
372
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fc/be/3e35429e915bc355cc69f522d79ccc122858ae038738ca3dcd8a7d51c854/great_tables-0.22.0.tar.gz", hash = "sha256:ccf031d90cab599328232bc32afadef215da1457dbc025d0972048cc2050fd57", size = 26110797, upload-time = "2026-06-12T01:07:28.678Z" }
|
|
302
373
|
wheels = [
|
|
303
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
374
|
+
{ url = "https://files.pythonhosted.org/packages/85/46/0fd6ca17e3b40b657af79aaf50e2e54f736f4ad5585e201da702c52c7c2a/great_tables-0.22.0-py3-none-any.whl", hash = "sha256:7a7f81c2d1eb66d88166e864ac820966c23f04e4ecb41eeca8f9df1e1b21764d", size = 1455612, upload-time = "2026-06-12T01:07:26.815Z" },
|
|
304
375
|
]
|
|
305
376
|
|
|
306
377
|
[[package]]
|
|
@@ -376,6 +447,33 @@ wheels = [
|
|
|
376
447
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
|
377
448
|
]
|
|
378
449
|
|
|
450
|
+
[[package]]
|
|
451
|
+
name = "multimark"
|
|
452
|
+
version = "0.2.0"
|
|
453
|
+
source = { registry = "https://pypi.org/simple" }
|
|
454
|
+
dependencies = [
|
|
455
|
+
{ name = "cffi" },
|
|
456
|
+
{ name = "click" },
|
|
457
|
+
]
|
|
458
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c2/6a/9e3029b89074f2ce705dd1a94eaf1372a49ac63ac47712a0f2410c574add/multimark-0.2.0.tar.gz", hash = "sha256:15a53111c52e0a65c200b57a5068a618734af52fc5d6512e8b9884fadbb2b8b8", size = 436111, upload-time = "2026-06-09T20:48:52.833Z" }
|
|
459
|
+
wheels = [
|
|
460
|
+
{ url = "https://files.pythonhosted.org/packages/c2/65/c080aa324ea650b78c7396a26395b0e8bbc7ace40651c3c9929e5222da85/multimark-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:483f51c416fa8a83cfe430b020afcdd08fa8e33a1335851c8ec9f617f9c561c7", size = 132784, upload-time = "2026-06-09T20:48:33.057Z" },
|
|
461
|
+
{ url = "https://files.pythonhosted.org/packages/8e/1e/d69bf6d93ad964e8d3efce6904c609eb4733f8bcfee33ceddb838a6e52df/multimark-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cf194a957dfeccb1d9d6cf38b787762c377895fdebbe9064533c6c1b13c65dd1", size = 130431, upload-time = "2026-06-09T20:48:33.928Z" },
|
|
462
|
+
{ url = "https://files.pythonhosted.org/packages/dc/fc/65505a111b35dcc0fccac6fc880e7b1d957606c8ead1d7792d6d292f2090/multimark-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd2ec35a273afe6a8f3101b4db1f850488c1be1af9525be53150129baf956db", size = 450398, upload-time = "2026-06-09T20:48:34.836Z" },
|
|
463
|
+
{ url = "https://files.pythonhosted.org/packages/00/42/346e9d7a49acde3240cde23492467c69b6f749ea932c0135dc490bc16cd4/multimark-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e05fe7c1badd7648e869eb5a65153d9d0b5dfbcdfaae795caaf0b77fbf92da73", size = 452478, upload-time = "2026-06-09T20:48:36.012Z" },
|
|
464
|
+
{ url = "https://files.pythonhosted.org/packages/0d/2d/e93c35a182373a9fbf56b78464372f9f909271ac65d879af51c0a6bc4b45/multimark-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:54cecb32ad96ca8672e320adc537c462807ca0bf9a3a421b68fba93c9708d5b1", size = 124924, upload-time = "2026-06-09T20:48:37.34Z" },
|
|
465
|
+
{ url = "https://files.pythonhosted.org/packages/c6/ee/b016667bcdb49630c8536146084d2726393c3123844021d18f559ac60145/multimark-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4be13eebff92579dfedda2d976e2d7cc6c4e1fd7d26c9b0a5a92de48f941146b", size = 132971, upload-time = "2026-06-09T20:48:38.477Z" },
|
|
466
|
+
{ url = "https://files.pythonhosted.org/packages/8c/ed/be18fa30222de97e206e1e3d4e4bd425a3c21dfe22b5650a81e015548c80/multimark-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:77ce50f531536f97007e090895327552f1a4a9847129f34f5afb6d6f8e00bce7", size = 130481, upload-time = "2026-06-09T20:48:39.517Z" },
|
|
467
|
+
{ url = "https://files.pythonhosted.org/packages/33/50/c5fbecf55ff3c05133d062653b22e63a14fa1a9bc890ac759d8826a48f36/multimark-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f093afd5696e58207bbc10854005f9f0ef4e34874437d266e53ed1b819f2cff", size = 451011, upload-time = "2026-06-09T20:48:40.531Z" },
|
|
468
|
+
{ url = "https://files.pythonhosted.org/packages/90/be/ae21b6d68eb6674086a60720c4d54367f4ef2ce963f33177f3710bdac9f6/multimark-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4498fea15ca97c578982f349d2521cd45895409e9d000f285e410ff0b82915af", size = 452975, upload-time = "2026-06-09T20:48:41.615Z" },
|
|
469
|
+
{ url = "https://files.pythonhosted.org/packages/28/85/0f361fa06cfaf62758fefef2d11141bbc58672a2c9d625674891b93fc23b/multimark-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ce9daae00ddc4f0f0cf07482d71b4f2bc95fac5aa81e8b3084252a8a23427dc3", size = 124934, upload-time = "2026-06-09T20:48:42.696Z" },
|
|
470
|
+
{ url = "https://files.pythonhosted.org/packages/36/75/0759e525cd4b53eeaa6b53b819d630fd2b1c5b73e6561e6b83a16a3ea4fc/multimark-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:390941f897850a4347d61fb4d8890810adc0c964a7d4565fc77c4cf30c0b5ea0", size = 132975, upload-time = "2026-06-09T20:48:43.567Z" },
|
|
471
|
+
{ url = "https://files.pythonhosted.org/packages/e4/f7/33f0c8b6814b2e034956df0bdb971fb64d88277fd1ddbf71ea5e135823f7/multimark-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a8527fe99aa56089228fe132540398c908e179a2a2259f4da006d5f6aab7217", size = 130476, upload-time = "2026-06-09T20:48:44.49Z" },
|
|
472
|
+
{ url = "https://files.pythonhosted.org/packages/fd/08/e6b1816b4ca68bf20a72f4784d924b12fe96083c0edbd4dc2e52e1795920/multimark-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b0bb3060b3a4b050bd1c6c75bbcbc8d0e705a70af73d3269c9637a2f4e08123", size = 450977, upload-time = "2026-06-09T20:48:45.341Z" },
|
|
473
|
+
{ url = "https://files.pythonhosted.org/packages/c4/4b/a5acdb28bc9e8ee638d8af74938780188efd5ae0102928ee33cd508cba72/multimark-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9b5542fee4d73498e3bc1055375a1370d6ca72916804a7c8484fa4409fd635b6", size = 452983, upload-time = "2026-06-09T20:48:46.322Z" },
|
|
474
|
+
{ url = "https://files.pythonhosted.org/packages/7f/65/8c550c71d1dc79ba04de57177a11e809b8a27ac4e56726ec487dccd68c95/multimark-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:c3ff54935aa0724390955c12a91d85d4e04664c2c8da147714226a4ac905b387", size = 124938, upload-time = "2026-06-09T20:48:47.507Z" },
|
|
475
|
+
]
|
|
476
|
+
|
|
379
477
|
[[package]]
|
|
380
478
|
name = "narwhals"
|
|
381
479
|
version = "2.17.0"
|
|
@@ -385,6 +483,19 @@ wheels = [
|
|
|
385
483
|
{ url = "https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl", hash = "sha256:2ac5307b7c2b275a7d66eeda906b8605e3d7a760951e188dcfff86e8ebe083dd", size = 444897, upload-time = "2026-02-23T09:44:32.006Z" },
|
|
386
484
|
]
|
|
387
485
|
|
|
486
|
+
[[package]]
|
|
487
|
+
name = "nokap"
|
|
488
|
+
version = "0.1.0"
|
|
489
|
+
source = { registry = "https://pypi.org/simple" }
|
|
490
|
+
dependencies = [
|
|
491
|
+
{ name = "click" },
|
|
492
|
+
{ name = "websockets" },
|
|
493
|
+
]
|
|
494
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b9/f7/a4f3d6f89773df40884bc10790977c80a9ea17ad526ea0b0cd35a703595c/nokap-0.1.0.tar.gz", hash = "sha256:63071cf5e7c4f0487532f6281c6ea85d90673d5cd835ffe5e64c913989742eb9", size = 77047, upload-time = "2026-06-08T23:19:17.959Z" }
|
|
495
|
+
wheels = [
|
|
496
|
+
{ url = "https://files.pythonhosted.org/packages/73/dc/c35af5ccf8dbf3268750c54a3c0830f486b8faec38c811aff92335aa99ba/nokap-0.1.0-py3-none-any.whl", hash = "sha256:36f2cae8d4251945dbe311ef68916c7eddfe51da490aabe0d68c5d13294b2539", size = 36233, upload-time = "2026-06-08T23:19:16.725Z" },
|
|
497
|
+
]
|
|
498
|
+
|
|
388
499
|
[[package]]
|
|
389
500
|
name = "packaging"
|
|
390
501
|
version = "26.0"
|
|
@@ -405,7 +516,7 @@ wheels = [
|
|
|
405
516
|
|
|
406
517
|
[[package]]
|
|
407
518
|
name = "pointblank"
|
|
408
|
-
version = "0.
|
|
519
|
+
version = "0.25.0"
|
|
409
520
|
source = { registry = "https://pypi.org/simple" }
|
|
410
521
|
dependencies = [
|
|
411
522
|
{ name = "click" },
|
|
@@ -418,9 +529,9 @@ dependencies = [
|
|
|
418
529
|
{ name = "rich" },
|
|
419
530
|
{ name = "typing-extensions" },
|
|
420
531
|
]
|
|
421
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
532
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1a/fe/ff52d7657fe7141d39ae8588e42eb0ea93f90e06ee296ce21a154c0bced2/pointblank-0.25.0.tar.gz", hash = "sha256:131909470d2734595b8ce4ea56ddf7634be5b64b9e18d78d008849109a5ca55f", size = 43398380, upload-time = "2026-06-12T16:52:38.28Z" }
|
|
422
533
|
wheels = [
|
|
423
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
534
|
+
{ url = "https://files.pythonhosted.org/packages/e8/7c/43ffba3f120c1c5ea2d00c13df1d9ecc24be7dde22e75262ce6ec0b094f0/pointblank-0.25.0-py3-none-any.whl", hash = "sha256:3998f6b9c3fd61b0aac487466969dbbc79e22bcd463c2da476439a9f5ad235c5", size = 19060424, upload-time = "2026-06-12T16:52:35.543Z" },
|
|
424
535
|
]
|
|
425
536
|
|
|
426
537
|
[[package]]
|
|
@@ -463,6 +574,15 @@ wheels = [
|
|
|
463
574
|
{ url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
|
|
464
575
|
]
|
|
465
576
|
|
|
577
|
+
[[package]]
|
|
578
|
+
name = "pycparser"
|
|
579
|
+
version = "3.0"
|
|
580
|
+
source = { registry = "https://pypi.org/simple" }
|
|
581
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
|
|
582
|
+
wheels = [
|
|
583
|
+
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
|
|
584
|
+
]
|
|
585
|
+
|
|
466
586
|
[[package]]
|
|
467
587
|
name = "pyfiglet"
|
|
468
588
|
version = "1.0.4"
|
|
@@ -711,6 +831,65 @@ wheels = [
|
|
|
711
831
|
{ url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
|
|
712
832
|
]
|
|
713
833
|
|
|
834
|
+
[[package]]
|
|
835
|
+
name = "websockets"
|
|
836
|
+
version = "16.0"
|
|
837
|
+
source = { registry = "https://pypi.org/simple" }
|
|
838
|
+
sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" }
|
|
839
|
+
wheels = [
|
|
840
|
+
{ url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" },
|
|
841
|
+
{ url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" },
|
|
842
|
+
{ url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" },
|
|
843
|
+
{ url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" },
|
|
844
|
+
{ url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" },
|
|
845
|
+
{ url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" },
|
|
846
|
+
{ url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" },
|
|
847
|
+
{ url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" },
|
|
848
|
+
{ url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" },
|
|
849
|
+
{ url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" },
|
|
850
|
+
{ url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" },
|
|
851
|
+
{ url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" },
|
|
852
|
+
{ url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" },
|
|
853
|
+
{ url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" },
|
|
854
|
+
{ url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" },
|
|
855
|
+
{ url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" },
|
|
856
|
+
{ url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" },
|
|
857
|
+
{ url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" },
|
|
858
|
+
{ url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" },
|
|
859
|
+
{ url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" },
|
|
860
|
+
{ url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" },
|
|
861
|
+
{ url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" },
|
|
862
|
+
{ url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" },
|
|
863
|
+
{ url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" },
|
|
864
|
+
{ url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" },
|
|
865
|
+
{ url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" },
|
|
866
|
+
{ url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" },
|
|
867
|
+
{ url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" },
|
|
868
|
+
{ url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" },
|
|
869
|
+
{ url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" },
|
|
870
|
+
{ url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" },
|
|
871
|
+
{ url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" },
|
|
872
|
+
{ url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" },
|
|
873
|
+
{ url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" },
|
|
874
|
+
{ url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" },
|
|
875
|
+
{ url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" },
|
|
876
|
+
{ url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" },
|
|
877
|
+
{ url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" },
|
|
878
|
+
{ url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" },
|
|
879
|
+
{ url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" },
|
|
880
|
+
{ url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" },
|
|
881
|
+
{ url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" },
|
|
882
|
+
{ url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" },
|
|
883
|
+
{ url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" },
|
|
884
|
+
{ url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" },
|
|
885
|
+
{ url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" },
|
|
886
|
+
{ url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" },
|
|
887
|
+
{ url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" },
|
|
888
|
+
{ url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" },
|
|
889
|
+
{ url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" },
|
|
890
|
+
{ url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
|
|
891
|
+
]
|
|
892
|
+
|
|
714
893
|
[[package]]
|
|
715
894
|
name = "zipp"
|
|
716
895
|
version = "3.23.0"
|
fauxdata_cli-0.1.4/.coverage
DELETED
|
Binary file
|
fauxdata_cli-0.1.4/tasks/todo.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Agent-friendly CLI improvements
|
|
2
|
-
|
|
3
|
-
## Obiettivo
|
|
4
|
-
Applicare i principi per CLI usabili dagli agenti AI.
|
|
5
|
-
|
|
6
|
-
## Fase 1 — Non-interactive `init`
|
|
7
|
-
|
|
8
|
-
- [ ] Aggiungere `--description`, `--rows`, `--format`, `--yes` come flag a `init_cmd` in `main.py`
|
|
9
|
-
- [ ] Aggiornare `run()` in `commands/init.py` per accettare questi parametri
|
|
10
|
-
- [ ] Rendere i prompt questionary fallback (solo quando il flag è None)
|
|
11
|
-
- [ ] `--yes` salta la conferma di sovrascrittura
|
|
12
|
-
|
|
13
|
-
## Fase 2 — Esempi in `--help`
|
|
14
|
-
|
|
15
|
-
- [ ] Aggiungere `epilog` con esempi a ogni comando in `main.py` (init, generate, validate, preview)
|
|
16
|
-
|
|
17
|
-
## Fase 3 — `--dry-run` per `generate`
|
|
18
|
-
|
|
19
|
-
- [ ] Aggiungere flag `--dry-run` a `generate_cmd` in `main.py`
|
|
20
|
-
- [ ] Implementare dry-run in `commands/generate.py`: mostra cosa farebbe senza scrivere file
|
|
21
|
-
|
|
22
|
-
## Fase 4 — Output strutturato su successo
|
|
23
|
-
|
|
24
|
-
- [ ] `generate`: output su successo mostra chiave=valore (output_path, format, rows)
|
|
25
|
-
- [ ] `init`: output su successo mostra schema_path
|
|
26
|
-
|
|
27
|
-
## Domande aperte
|
|
28
|
-
|
|
29
|
-
- Aggiungere anche `--quiet` / `--json` per output machine-readable?
|
|
30
|
-
- `validate` e `preview` hanno già flag, sufficienti?
|
|
31
|
-
|
|
32
|
-
## Review
|
|
33
|
-
|
|
34
|
-
- Tutte e 4 le fasi completate; 79/79 test passano, coverage 82%
|
|
35
|
-
- `init` ora fully non-interactive con `--name --description --rows --format --yes`
|
|
36
|
-
- `generate` ha `--dry-run` e output strutturato chiave: valore
|
|
37
|
-
- Ogni comando ha esempi nel `--help` via `epilog`
|
|
38
|
-
- Venv ricreato con `--clear` (cartella rinominata da `real_fake_datasets`)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|