minimindslab-csv-normalize 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MiniMindsLab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: minimindslab-csv-normalize
3
+ Version: 0.1.0
4
+ Summary: Normalize small CSV text into stable, comparison-friendly output with controllable header ordering, trimming, and line endings.
5
+ Author: MiniMindsLab
6
+ License: MIT
7
+ Keywords: csv,normalize,deterministic,cli
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Classifier: Topic :: Text Processing
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # MiniMindsLab CSV Normalize
22
+
23
+ Deterministically normalize small CSV text for stable diffs, tests, and reviews.
24
+
25
+ ## Features
26
+
27
+ - Trims surrounding whitespace in headers and cell values.
28
+ - Normalizes mixed line endings to `\n`.
29
+ - Optional stable header sorting.
30
+ - Deterministic output for identical logical input.
31
+ - Library API and CLI.
32
+
33
+ ## Library Usage
34
+
35
+ ```python
36
+ from minimindslab_csv_normalize import normalize_csv
37
+
38
+ raw = " Name , Age \r\n Alice , 30 \r\n"
39
+ out = normalize_csv(raw)
40
+ print(out)
41
+ # Age,Name\n30,Alice\n
42
+ # Keep original header order
43
+ out2 = normalize_csv(raw, sort_headers=False)
44
+ print(out2)
45
+ # Name,Age\nAlice,30\n
46
+ ```
47
+
48
+ ## CLI Usage
49
+
50
+ From stdin:
51
+
52
+ ```bash
53
+ echo " Name , Age \r\n Alice , 30 " | miniminds-csv-normalize
54
+ ```
55
+
56
+ From file:
57
+
58
+ ```bash
59
+ miniminds-csv-normalize --input sample.csv
60
+ ```
61
+
62
+ Disable header sorting:
63
+
64
+ ```bash
65
+ miniminds-csv-normalize --no-sort-headers --input sample.csv
66
+ ```
@@ -0,0 +1,46 @@
1
+ # MiniMindsLab CSV Normalize
2
+
3
+ Deterministically normalize small CSV text for stable diffs, tests, and reviews.
4
+
5
+ ## Features
6
+
7
+ - Trims surrounding whitespace in headers and cell values.
8
+ - Normalizes mixed line endings to `\n`.
9
+ - Optional stable header sorting.
10
+ - Deterministic output for identical logical input.
11
+ - Library API and CLI.
12
+
13
+ ## Library Usage
14
+
15
+ ```python
16
+ from minimindslab_csv_normalize import normalize_csv
17
+
18
+ raw = " Name , Age \r\n Alice , 30 \r\n"
19
+ out = normalize_csv(raw)
20
+ print(out)
21
+ # Age,Name\n30,Alice\n
22
+ # Keep original header order
23
+ out2 = normalize_csv(raw, sort_headers=False)
24
+ print(out2)
25
+ # Name,Age\nAlice,30\n
26
+ ```
27
+
28
+ ## CLI Usage
29
+
30
+ From stdin:
31
+
32
+ ```bash
33
+ echo " Name , Age \r\n Alice , 30 " | miniminds-csv-normalize
34
+ ```
35
+
36
+ From file:
37
+
38
+ ```bash
39
+ miniminds-csv-normalize --input sample.csv
40
+ ```
41
+
42
+ Disable header sorting:
43
+
44
+ ```bash
45
+ miniminds-csv-normalize --no-sort-headers --input sample.csv
46
+ ```
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "minimindslab-csv-normalize"
7
+ version = "0.1.0"
8
+ description = "Normalize small CSV text into stable, comparison-friendly output with controllable header ordering, trimming, and line endings."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "MiniMindsLab" }]
13
+ dependencies = []
14
+ keywords = ["csv", "normalize", "deterministic", "cli"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Testing",
23
+ "Topic :: Text Processing"
24
+ ]
25
+
26
+ [project.scripts]
27
+ miniminds-csv-normalize = "minimindslab_csv_normalize.__main__:main"
28
+
29
+ [tool.setuptools]
30
+ package-dir = {"" = "src"}
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """MiniMindsLab CSV Normalize."""
2
+
3
+ from .core import normalize_csv
4
+
5
+ __all__ = ["normalize_csv"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ from .core import normalize_csv
7
+
8
+
9
+ def build_parser() -> argparse.ArgumentParser:
10
+ parser = argparse.ArgumentParser(description="Normalize small CSV text deterministically.")
11
+ parser.add_argument("--input", "-i", help="Path to input CSV file. If omitted, read from stdin.")
12
+ parser.add_argument("--no-sort-headers", action="store_true", help="Preserve original header order.")
13
+ parser.add_argument("--newline", choices=["lf", "crlf"], default="lf", help="Output newline style.")
14
+ return parser
15
+
16
+
17
+ def main() -> int:
18
+ parser = build_parser()
19
+ args = parser.parse_args()
20
+
21
+ if args.input:
22
+ with open(args.input, "r", encoding="utf-8", newline="") as f:
23
+ text = f.read()
24
+ else:
25
+ text = sys.stdin.read()
26
+
27
+ nl = "\n" if args.newline == "lf" else "\r\n"
28
+ result = normalize_csv(text, sort_headers=not args.no_sort_headers, newline=nl)
29
+ sys.stdout.write(result)
30
+ return 0
31
+
32
+
33
+ if __name__ == "__main__":
34
+ raise SystemExit(main())
@@ -0,0 +1,60 @@
1
+ """Core CSV normalization logic."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ import io
7
+ from typing import List
8
+
9
+
10
+ def _normalize_newlines(text: str) -> str:
11
+ return text.replace("\r\n", "\n").replace("\r", "\n")
12
+
13
+
14
+ def normalize_csv(text: str, *, sort_headers: bool = True, newline: str = "\n") -> str:
15
+ """Normalize CSV text into stable output.
16
+
17
+ - Normalizes line endings in input.
18
+ - Trims leading/trailing whitespace for all cells.
19
+ - Optionally sorts headers alphabetically for deterministic column order.
20
+ - Emits output with a consistent newline and trailing newline when non-empty.
21
+ """
22
+ if not isinstance(text, str):
23
+ raise TypeError("text must be a str")
24
+ if newline not in {"\n", "\r\n"}:
25
+ raise ValueError("newline must be '\\n' or '\\r\\n'")
26
+
27
+ src = _normalize_newlines(text)
28
+ if src.strip() == "":
29
+ return ""
30
+
31
+ reader = csv.reader(io.StringIO(src))
32
+ rows: List[List[str]] = [[cell.strip() for cell in row] for row in reader]
33
+ if not rows:
34
+ return ""
35
+
36
+ header = rows[0]
37
+ data = rows[1:]
38
+
39
+ if sort_headers:
40
+ indices = sorted(range(len(header)), key=lambda i: (header[i], i))
41
+ header = [header[i] for i in indices]
42
+
43
+ def reorder(row: List[str]) -> List[str]:
44
+ padded = row + [""] * (len(indices) - len(row))
45
+ return [padded[i] if i < len(padded) else "" for i in indices]
46
+
47
+ data = [reorder(r) for r in data]
48
+ else:
49
+ data = [r + [""] * (len(header) - len(r)) if len(r) < len(header) else r[: len(header)] for r in data]
50
+
51
+ output = io.StringIO()
52
+ writer = csv.writer(output, lineterminator="\n")
53
+ writer.writerow(header)
54
+ for row in data:
55
+ writer.writerow(row)
56
+
57
+ rendered = output.getvalue()
58
+ if newline != "\n":
59
+ rendered = rendered.replace("\n", newline)
60
+ return rendered
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: minimindslab-csv-normalize
3
+ Version: 0.1.0
4
+ Summary: Normalize small CSV text into stable, comparison-friendly output with controllable header ordering, trimming, and line endings.
5
+ Author: MiniMindsLab
6
+ License: MIT
7
+ Keywords: csv,normalize,deterministic,cli
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Classifier: Topic :: Text Processing
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # MiniMindsLab CSV Normalize
22
+
23
+ Deterministically normalize small CSV text for stable diffs, tests, and reviews.
24
+
25
+ ## Features
26
+
27
+ - Trims surrounding whitespace in headers and cell values.
28
+ - Normalizes mixed line endings to `\n`.
29
+ - Optional stable header sorting.
30
+ - Deterministic output for identical logical input.
31
+ - Library API and CLI.
32
+
33
+ ## Library Usage
34
+
35
+ ```python
36
+ from minimindslab_csv_normalize import normalize_csv
37
+
38
+ raw = " Name , Age \r\n Alice , 30 \r\n"
39
+ out = normalize_csv(raw)
40
+ print(out)
41
+ # Age,Name\n30,Alice\n
42
+ # Keep original header order
43
+ out2 = normalize_csv(raw, sort_headers=False)
44
+ print(out2)
45
+ # Name,Age\nAlice,30\n
46
+ ```
47
+
48
+ ## CLI Usage
49
+
50
+ From stdin:
51
+
52
+ ```bash
53
+ echo " Name , Age \r\n Alice , 30 " | miniminds-csv-normalize
54
+ ```
55
+
56
+ From file:
57
+
58
+ ```bash
59
+ miniminds-csv-normalize --input sample.csv
60
+ ```
61
+
62
+ Disable header sorting:
63
+
64
+ ```bash
65
+ miniminds-csv-normalize --no-sort-headers --input sample.csv
66
+ ```
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/minimindslab_csv_normalize/__init__.py
5
+ src/minimindslab_csv_normalize/__main__.py
6
+ src/minimindslab_csv_normalize/core.py
7
+ src/minimindslab_csv_normalize.egg-info/PKG-INFO
8
+ src/minimindslab_csv_normalize.egg-info/SOURCES.txt
9
+ src/minimindslab_csv_normalize.egg-info/dependency_links.txt
10
+ src/minimindslab_csv_normalize.egg-info/entry_points.txt
11
+ src/minimindslab_csv_normalize.egg-info/top_level.txt
12
+ tests/test_normalize.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ miniminds-csv-normalize = minimindslab_csv_normalize.__main__:main
@@ -0,0 +1,75 @@
1
+ import importlib
2
+ import os
3
+ import tempfile
4
+ import unittest
5
+
6
+ from minimindslab_csv_normalize import normalize_csv
7
+
8
+
9
+ class TestNormalizeCSV(unittest.TestCase):
10
+ def test_trim_and_stable_form(self):
11
+ text = " Name , Age \n Alice , 30 \n"
12
+ got = normalize_csv(text)
13
+ self.assertEqual(got, "Age,Name\n30,Alice\n")
14
+
15
+ def test_mixed_newlines_deterministic(self):
16
+ text = "b,a\r\n2,1\r3,4\n"
17
+ got = normalize_csv(text)
18
+ self.assertEqual(got, "a,b\n1,2\n4,3\n")
19
+
20
+ def test_idempotent(self):
21
+ text = " z , y \n 1 , 2 \n"
22
+ a = normalize_csv(text)
23
+ b = normalize_csv(text)
24
+ self.assertEqual(a, b)
25
+
26
+ def test_negative_newline_option(self):
27
+ with self.assertRaises(ValueError):
28
+ normalize_csv("a,b\n1,2\n", newline="x")
29
+
30
+ def test_negative_type(self):
31
+ with self.assertRaises(TypeError):
32
+ normalize_csv(None) # type: ignore[arg-type]
33
+
34
+
35
+ class TestCLI(unittest.TestCase):
36
+ def _run_main(self, args, input_text=None):
37
+ old_argv = os.sys.argv
38
+ old_stdin = os.sys.stdin
39
+ old_stdout = os.sys.stdout
40
+ try:
41
+ mod = importlib.import_module("minimindslab_csv_normalize.__main__")
42
+ import io
43
+
44
+ os.sys.argv = ["miniminds-csv-normalize", *args]
45
+ os.sys.stdin = io.StringIO(input_text if input_text is not None else "")
46
+ captured = io.StringIO()
47
+ os.sys.stdout = captured
48
+ code = mod.main()
49
+ return code, captured.getvalue()
50
+ finally:
51
+ os.sys.argv = old_argv
52
+ os.sys.stdin = old_stdin
53
+ os.sys.stdout = old_stdout
54
+
55
+ def test_cli_stdin_matches_library(self):
56
+ text = " Name , Age \r\n Alice , 30 \n"
57
+ code, out = self._run_main([], input_text=text)
58
+ self.assertEqual(code, 0)
59
+ self.assertEqual(out, normalize_csv(text))
60
+
61
+ def test_cli_file_matches_library(self):
62
+ text = "b,a\n2,1\n"
63
+ with tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False) as tmp:
64
+ tmp.write(text)
65
+ path = tmp.name
66
+ try:
67
+ code, out = self._run_main(["--input", path])
68
+ self.assertEqual(code, 0)
69
+ self.assertEqual(out, normalize_csv(text))
70
+ finally:
71
+ os.unlink(path)
72
+
73
+
74
+ if __name__ == "__main__":
75
+ unittest.main()