minimindslab-csv-sniff 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,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: minimindslab-csv-sniff
3
+ Version: 0.1.0
4
+ Summary: Infer a CSV dialect from a short text sample and normalize it into a structured, testable description (delimiter, quote character, escape character, line terminator style, and header-likelihood).
5
+ Author: MiniMindsLab
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # MiniMindsLab CSV Sniff
13
+
14
+ MiniMindsLab CSV Sniff is a tiny, offline helper for inferring CSV dialect details from short text samples.
15
+
16
+ ## Features
17
+
18
+ - Infers delimiter (comma, tab, semicolon, pipe, or fallback)
19
+ - Reports quote character and escape character assumptions
20
+ - Classifies line terminator style (`lf`, `crlf`, `cr`, or `mixed`)
21
+ - Estimates whether a header row is likely
22
+ - Deterministic output suitable for tests and serialization
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from minimindslab_csv_sniff import sniff_csv
28
+
29
+ sample = "name,age\nAlice,30\nBob,41\n"
30
+ result = sniff_csv(sample)
31
+ print(result)
32
+ # {
33
+ # 'delimiter': ',',
34
+ # 'quotechar': '"',
35
+ # 'escapechar': None,
36
+ # 'lineterminator': 'lf',
37
+ # 'header_likely': True
38
+ # }
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### `sniff_csv(sample: str) -> dict`
44
+
45
+ Raises `ValueError` when input is empty or whitespace-only.
46
+
47
+ Returns a dictionary with keys:
48
+
49
+ - `delimiter`
50
+ - `quotechar`
51
+ - `escapechar`
52
+ - `lineterminator`
53
+ - `header_likely`
@@ -0,0 +1,42 @@
1
+ # MiniMindsLab CSV Sniff
2
+
3
+ MiniMindsLab CSV Sniff is a tiny, offline helper for inferring CSV dialect details from short text samples.
4
+
5
+ ## Features
6
+
7
+ - Infers delimiter (comma, tab, semicolon, pipe, or fallback)
8
+ - Reports quote character and escape character assumptions
9
+ - Classifies line terminator style (`lf`, `crlf`, `cr`, or `mixed`)
10
+ - Estimates whether a header row is likely
11
+ - Deterministic output suitable for tests and serialization
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ from minimindslab_csv_sniff import sniff_csv
17
+
18
+ sample = "name,age\nAlice,30\nBob,41\n"
19
+ result = sniff_csv(sample)
20
+ print(result)
21
+ # {
22
+ # 'delimiter': ',',
23
+ # 'quotechar': '"',
24
+ # 'escapechar': None,
25
+ # 'lineterminator': 'lf',
26
+ # 'header_likely': True
27
+ # }
28
+ ```
29
+
30
+ ## API
31
+
32
+ ### `sniff_csv(sample: str) -> dict`
33
+
34
+ Raises `ValueError` when input is empty or whitespace-only.
35
+
36
+ Returns a dictionary with keys:
37
+
38
+ - `delimiter`
39
+ - `quotechar`
40
+ - `escapechar`
41
+ - `lineterminator`
42
+ - `header_likely`
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "minimindslab-csv-sniff"
7
+ version = "0.1.0"
8
+ description = "Infer a CSV dialect from a short text sample and normalize it into a structured, testable description (delimiter, quote character, escape character, line terminator style, and header-likelihood)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "MiniMindsLab" }]
13
+ dependencies = []
14
+
15
+ [tool.setuptools]
16
+ package-dir = {"" = "src"}
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,103 @@
1
+ """MiniMindsLab CSV Sniff."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ from io import StringIO
7
+ from typing import Any
8
+
9
+ __all__ = ["sniff_csv"]
10
+
11
+
12
+ def _line_terminator_style(sample: str) -> str:
13
+ has_crlf = "\r\n" in sample
14
+ cr_only_count = sample.count("\r") - sample.count("\r\n")
15
+ has_cr_only = cr_only_count > 0
16
+ has_lf = sample.count("\n") > sample.count("\r\n") or ("\n" in sample and not has_crlf)
17
+
18
+ kinds = sum([1 if has_crlf else 0, 1 if has_cr_only else 0, 1 if has_lf else 0])
19
+ if kinds > 1:
20
+ return "mixed"
21
+ if has_crlf:
22
+ return "crlf"
23
+ if has_cr_only:
24
+ return "cr"
25
+ return "lf"
26
+
27
+
28
+ def _header_likely(lines: list[list[str]]) -> bool:
29
+ if len(lines) < 2 or not lines[0]:
30
+ return False
31
+
32
+ first = lines[0]
33
+ rest = lines[1:]
34
+
35
+ def is_number(text: str) -> bool:
36
+ t = text.strip()
37
+ if not t:
38
+ return False
39
+ try:
40
+ float(t)
41
+ return True
42
+ except ValueError:
43
+ return False
44
+
45
+ score = 0
46
+ for col in range(len(first)):
47
+ head = first[col].strip()
48
+ if not head:
49
+ continue
50
+ head_is_num = is_number(head)
51
+
52
+ numeric_below = 0
53
+ nonempty_below = 0
54
+ for row in rest:
55
+ if col >= len(row):
56
+ continue
57
+ v = row[col].strip()
58
+ if not v:
59
+ continue
60
+ nonempty_below += 1
61
+ if is_number(v):
62
+ numeric_below += 1
63
+
64
+ if nonempty_below == 0:
65
+ continue
66
+ if (not head_is_num) and (numeric_below >= max(1, nonempty_below // 2)):
67
+ score += 1
68
+
69
+ return score >= max(1, len(first) // 3)
70
+
71
+
72
+ def sniff_csv(sample: str) -> dict[str, Any]:
73
+ """Infer CSV formatting details from a short sample.
74
+
75
+ Returns keys: delimiter, quotechar, escapechar, lineterminator, header_likely.
76
+ """
77
+ if not isinstance(sample, str):
78
+ raise ValueError("sample must be a non-empty string")
79
+ if sample.strip() == "":
80
+ raise ValueError("sample must not be empty or whitespace-only")
81
+
82
+ sniffer = csv.Sniffer()
83
+ candidates = [",", "\t", ";", "|", ":"]
84
+ try:
85
+ dialect = sniffer.sniff(sample, delimiters="".join(candidates))
86
+ delimiter = dialect.delimiter
87
+ quotechar = dialect.quotechar or '"'
88
+ escapechar = dialect.escapechar
89
+ except csv.Error:
90
+ delimiter = ","
91
+ quotechar = '"'
92
+ escapechar = None
93
+
94
+ reader = csv.reader(StringIO(sample), delimiter=delimiter, quotechar=quotechar)
95
+ rows = [row for row in reader if row]
96
+
97
+ return {
98
+ "delimiter": delimiter,
99
+ "quotechar": quotechar,
100
+ "escapechar": escapechar,
101
+ "lineterminator": _line_terminator_style(sample),
102
+ "header_likely": _header_likely(rows),
103
+ }
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: minimindslab-csv-sniff
3
+ Version: 0.1.0
4
+ Summary: Infer a CSV dialect from a short text sample and normalize it into a structured, testable description (delimiter, quote character, escape character, line terminator style, and header-likelihood).
5
+ Author: MiniMindsLab
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # MiniMindsLab CSV Sniff
13
+
14
+ MiniMindsLab CSV Sniff is a tiny, offline helper for inferring CSV dialect details from short text samples.
15
+
16
+ ## Features
17
+
18
+ - Infers delimiter (comma, tab, semicolon, pipe, or fallback)
19
+ - Reports quote character and escape character assumptions
20
+ - Classifies line terminator style (`lf`, `crlf`, `cr`, or `mixed`)
21
+ - Estimates whether a header row is likely
22
+ - Deterministic output suitable for tests and serialization
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from minimindslab_csv_sniff import sniff_csv
28
+
29
+ sample = "name,age\nAlice,30\nBob,41\n"
30
+ result = sniff_csv(sample)
31
+ print(result)
32
+ # {
33
+ # 'delimiter': ',',
34
+ # 'quotechar': '"',
35
+ # 'escapechar': None,
36
+ # 'lineterminator': 'lf',
37
+ # 'header_likely': True
38
+ # }
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### `sniff_csv(sample: str) -> dict`
44
+
45
+ Raises `ValueError` when input is empty or whitespace-only.
46
+
47
+ Returns a dictionary with keys:
48
+
49
+ - `delimiter`
50
+ - `quotechar`
51
+ - `escapechar`
52
+ - `lineterminator`
53
+ - `header_likely`
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/minimindslab_csv_sniff/__init__.py
5
+ src/minimindslab_csv_sniff.egg-info/PKG-INFO
6
+ src/minimindslab_csv_sniff.egg-info/SOURCES.txt
7
+ src/minimindslab_csv_sniff.egg-info/dependency_links.txt
8
+ src/minimindslab_csv_sniff.egg-info/top_level.txt
9
+ tests/test_csv_sniff.py
@@ -0,0 +1,37 @@
1
+ import unittest
2
+
3
+ from minimindslab_csv_sniff import sniff_csv
4
+
5
+
6
+ class TestCsvSniff(unittest.TestCase):
7
+ def test_comma_with_header(self):
8
+ sample = "name,age\nAlice,30\nBob,41\n"
9
+ result = sniff_csv(sample)
10
+ self.assertEqual(result["delimiter"], ",")
11
+ self.assertEqual(result["quotechar"], '"')
12
+ self.assertTrue(result["header_likely"])
13
+
14
+ def test_tab_separated(self):
15
+ sample = "name\tage\nAlice\t30\nBob\t41\n"
16
+ result = sniff_csv(sample)
17
+ self.assertEqual(result["delimiter"], "\t")
18
+
19
+ def test_empty_or_whitespace_raises(self):
20
+ with self.assertRaisesRegex(ValueError, "empty or whitespace-only"):
21
+ sniff_csv("")
22
+ with self.assertRaisesRegex(ValueError, "empty or whitespace-only"):
23
+ sniff_csv(" \n\t")
24
+
25
+ def test_deterministic_output(self):
26
+ sample = "city;country\nParis;FR\nRome;IT\n"
27
+ a = sniff_csv(sample)
28
+ b = sniff_csv(sample)
29
+ self.assertEqual(a, b)
30
+
31
+ def test_non_string_input_raises(self):
32
+ with self.assertRaisesRegex(ValueError, "non-empty string"):
33
+ sniff_csv(None) # type: ignore[arg-type]
34
+
35
+
36
+ if __name__ == "__main__":
37
+ unittest.main()