kryptorious-dataforge 1.0.0__py3-none-any.whl

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.
dataforge/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ """DataForge — Universal data format converter."""
2
+ __version__ = "1.0.0"
dataforge/cli.py ADDED
@@ -0,0 +1,256 @@
1
+ """DataForge CLI — Convert between any data format."""
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import click
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+
13
+ console = Console()
14
+
15
+ FORMATS = ["json", "yaml", "yml", "toml", "csv", "xml"]
16
+
17
+
18
+ def _detect_format(path: str) -> str:
19
+ ext = Path(path).suffix.lower().lstrip(".")
20
+ if ext in FORMATS:
21
+ return "yaml" if ext == "yml" else ext
22
+ return ext
23
+
24
+
25
+ def _read_file(path: str, fmt: str = None):
26
+ fmt = fmt or _detect_format(path)
27
+ content = Path(path).read_text(encoding="utf-8")
28
+
29
+ if fmt == "json":
30
+ return json.loads(content)
31
+ elif fmt in ("yaml", "yml"):
32
+ import yaml
33
+ return yaml.safe_load(content)
34
+ elif fmt == "toml":
35
+ try:
36
+ import tomllib
37
+ except ImportError:
38
+ import tomli as tomllib
39
+ return tomllib.loads(content)
40
+ elif fmt == "csv":
41
+ import csv, io
42
+ reader = csv.DictReader(io.StringIO(content))
43
+ return [row for row in reader]
44
+ elif fmt == "xml":
45
+ import xml.etree.ElementTree as ET
46
+ return _xml_to_dict(ET.fromstring(content))
47
+ else:
48
+ raise ValueError(f"Unsupported format: {fmt}")
49
+
50
+
51
+ def _write_file(data, path: str, fmt: str = None):
52
+ fmt = fmt or _detect_format(path)
53
+
54
+ if fmt == "json":
55
+ content = json.dumps(data, indent=2, ensure_ascii=False)
56
+ elif fmt in ("yaml", "yml"):
57
+ import yaml
58
+ content = yaml.dump(data, default_flow_style=False, allow_unicode=True, sort_keys=False)
59
+ elif fmt == "toml":
60
+ raise click.ClickException("TOML output requires premium version. Upgrade at https://kryptorious.gumroad.com/l/jbvet")
61
+ elif fmt == "csv":
62
+ if not isinstance(data, list):
63
+ raise ValueError("CSV output requires list data")
64
+ import csv, io
65
+ output = io.StringIO()
66
+ if data:
67
+ writer = csv.DictWriter(output, fieldnames=data[0].keys())
68
+ writer.writeheader()
69
+ writer.writerows(data)
70
+ content = output.getvalue()
71
+ elif fmt == "xml":
72
+ content = _dict_to_xml(data)
73
+ else:
74
+ raise ValueError(f"Unsupported format: {fmt}")
75
+
76
+ Path(path).write_text(content, encoding="utf-8")
77
+ return content
78
+
79
+
80
+ def _xml_to_dict(element):
81
+ result = {}
82
+ if element.attrib:
83
+ result["@attributes"] = element.attrib
84
+ for child in element:
85
+ child_dict = _xml_to_dict(child)
86
+ tag = child.tag
87
+ if tag in result:
88
+ if not isinstance(result[tag], list):
89
+ result[tag] = [result[tag]]
90
+ result[tag].append(child_dict)
91
+ else:
92
+ result[tag] = child_dict
93
+ text = element.text.strip() if element.text else ""
94
+ if not result and text:
95
+ return text
96
+ if text:
97
+ result["#text"] = text
98
+ return result
99
+
100
+
101
+ def _dict_to_xml(data, root_tag="root"):
102
+ import xml.etree.ElementTree as ET
103
+ import xml.dom.minidom as minidom
104
+
105
+ def _build(element, data):
106
+ if isinstance(data, dict):
107
+ for key, value in data.items():
108
+ if key.startswith("@"):
109
+ element.set(key[1:], str(value))
110
+ elif key == "#text":
111
+ element.text = str(value)
112
+ else:
113
+ child = ET.SubElement(element, key)
114
+ _build(child, value)
115
+ elif isinstance(data, list):
116
+ for item in data:
117
+ child = ET.SubElement(element, "item")
118
+ _build(child, item)
119
+ else:
120
+ element.text = str(data)
121
+
122
+ root = ET.Element(root_tag)
123
+ if isinstance(data, dict) and len(data) == 1:
124
+ tag, content = next(iter(data.items()))
125
+ root = ET.Element(tag)
126
+ _build(root, content)
127
+ else:
128
+ _build(root, data)
129
+
130
+ return minidom.parseString(ET.tostring(root, encoding="unicode")).toprettyxml(indent=" ")
131
+
132
+
133
+ @click.group()
134
+ @click.version_option(version="1.0.0", prog_name="dataforge")
135
+ def main():
136
+ """DataForge — Convert between JSON, YAML, TOML, CSV, XML.
137
+
138
+ One command to convert, validate, and merge any data format.
139
+ """
140
+ pass
141
+
142
+
143
+ @main.command()
144
+ @click.argument("input_path")
145
+ @click.argument("output_path")
146
+ @click.option("--from", "-f", "from_fmt", help="Input format (auto-detect if omitted)")
147
+ @click.option("--to", "-t", "to_fmt", help="Output format (auto-detect if omitted)")
148
+ @click.option("--pretty/--compact", default=True, help="Pretty-print output")
149
+ def convert(input_path, output_path, from_fmt, to_fmt, pretty):
150
+ """Convert between data formats.
151
+
152
+ \b
153
+ Examples:
154
+ dataforge convert data.json output.yaml
155
+ dataforge convert data.csv output.json
156
+ dataforge convert config.toml config.json
157
+ dataforge convert data.json output.xml
158
+ """
159
+ in_fmt = from_fmt or _detect_format(input_path)
160
+ out_fmt = to_fmt or _detect_format(output_path)
161
+
162
+ console.print()
163
+ console.print(Panel(f"[bold]DataForge Convert[/bold] — [cyan]{in_fmt}[/cyan] → [green]{out_fmt}[/green]",
164
+ border_style="blue"))
165
+
166
+ try:
167
+ console.print(f"Reading [cyan]{input_path}[/cyan]...")
168
+ data = _read_file(input_path, in_fmt)
169
+ console.print(f"Writing [green]{output_path}[/green]...")
170
+ _write_file(data, output_path, out_fmt)
171
+ size = os.path.getsize(output_path)
172
+ console.print(f"[green]✓[/green] Converted {_format_size(size)} — {output_path}")
173
+ except Exception as e:
174
+ console.print(f"[red]Error:[/red] {e}")
175
+ raise SystemExit(1)
176
+
177
+
178
+ @main.command()
179
+ @click.argument("path")
180
+ @click.option("--format", "-f", "fmt", help="Format (auto-detect if omitted)")
181
+ def validate(path, fmt):
182
+ """Validate a data file's syntax.
183
+
184
+ \b
185
+ Examples:
186
+ dataforge validate config.yaml
187
+ dataforge validate data.json
188
+ """
189
+ fmt = fmt or _detect_format(path)
190
+ console.print()
191
+ console.print(Panel(f"[bold]DataForge Validate[/bold] — [cyan]{path}[/cyan] ({fmt})",
192
+ border_style="blue"))
193
+
194
+ try:
195
+ data = _read_file(path, fmt)
196
+ console.print(f"[green]✓[/green] Valid {fmt.upper()} — {_summarize(data)}")
197
+ except Exception as e:
198
+ console.print(f"[red]✗[/red] Invalid {fmt.upper()}: {e}")
199
+ raise SystemExit(1)
200
+
201
+
202
+ @main.command()
203
+ @click.argument("files", nargs=-1)
204
+ @click.argument("output")
205
+ @click.option("--format", "-f", "fmt", default="json", help="Output format")
206
+ def merge(files, output, fmt):
207
+ """Merge multiple data files into one (premium feature).
208
+
209
+ \b
210
+ Example:
211
+ dataforge merge a.json b.json merged.json
212
+ """
213
+ console.print()
214
+ console.print("[yellow]DataForge merge is a premium feature.[/yellow]")
215
+ console.print("Upgrade at https://kryptorious.gumroad.com/l/jbvet")
216
+ console.print()
217
+ console.print(f"Would merge {len(files)} files → {output} ({fmt})")
218
+
219
+
220
+ @main.command()
221
+ @click.argument("path")
222
+ def view(path):
223
+ """Pretty-print any data file to terminal.
224
+
225
+ \b
226
+ Examples:
227
+ dataforge view config.yaml
228
+ dataforge view data.json
229
+ """
230
+ fmt = _detect_format(path)
231
+ data = _read_file(path, fmt)
232
+ console.print()
233
+ console.print(Panel(f"[bold]{path}[/bold] ({fmt})", border_style="blue"))
234
+ console.print_json(json.dumps(data)) if isinstance(data, (dict, list)) else console.print(data)
235
+
236
+
237
+ def _format_size(size: int) -> str:
238
+ if size < 1024:
239
+ return f"{size} B"
240
+ elif size < 1024 * 1024:
241
+ return f"{size / 1024:.1f} KB"
242
+ else:
243
+ return f"{size / (1024 * 1024):.1f} MB"
244
+
245
+
246
+ def _summarize(data) -> str:
247
+ if isinstance(data, dict):
248
+ return f"{len(data)} keys"
249
+ elif isinstance(data, list):
250
+ return f"{len(data)} items"
251
+ else:
252
+ return str(type(data).__name__)
253
+
254
+
255
+ if __name__ == "__main__":
256
+ main()
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: kryptorious-dataforge
3
+ Version: 1.0.0
4
+ Summary: Universal data format converter — JSON, YAML, TOML, CSV, XML. Convert, validate, merge.
5
+ Author: Kryptorious Quantum Biosciences, Inc.
6
+ License: MIT
7
+ Project-URL: Homepage, https://devflow.sh
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: click>=8.0
11
+ Requires-Dist: rich>=13.0
12
+ Requires-Dist: pyyaml>=6.0
13
+ Requires-Dist: tomli>=2.0; python_version < "3.11"
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=7.0; extra == "dev"
16
+ Requires-Dist: black>=23.0; extra == "dev"
@@ -0,0 +1,7 @@
1
+ dataforge/__init__.py,sha256=xotzkkHKwVHBqIs764D4qGeS_mNOp-YwliP91KzGMJM,75
2
+ dataforge/cli.py,sha256=smZmucO17J7LQZnaVQtRlxinewqNBlAG0LXyC0u3maw,7857
3
+ kryptorious_dataforge-1.0.0.dist-info/METADATA,sha256=_ivDqgdFJiR_fF_C8kozgBIxEwvUQhzfh0eM6qzz7W4,580
4
+ kryptorious_dataforge-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
5
+ kryptorious_dataforge-1.0.0.dist-info/entry_points.txt,sha256=wI6KGV1q7lE0W1CYtkOYsPDAXdZhZfOfqbhrbtbFK1k,49
6
+ kryptorious_dataforge-1.0.0.dist-info/top_level.txt,sha256=xv3CY-CdHCuuvu_sV6g-QJzpRJM7YFJwvQunsUpM0As,10
7
+ kryptorious_dataforge-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dataforge = dataforge.cli:main
@@ -0,0 +1 @@
1
+ dataforge