TSVZ 3.35__tar.gz → 3.37__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.
- {tsvz-3.35 → tsvz-3.37}/PKG-INFO +6 -1
- {tsvz-3.35 → tsvz-3.37}/TSVZ.egg-info/PKG-INFO +6 -1
- {tsvz-3.35 → tsvz-3.37}/TSVZ.egg-info/SOURCES.txt +3 -1
- tsvz-3.37/TSVZ.egg-info/requires.txt +4 -0
- {tsvz-3.35 → tsvz-3.37}/TSVZ.py +26 -10
- {tsvz-3.35 → tsvz-3.37}/setup.py +8 -0
- tsvz-3.37/tests/test_regression.py +104 -0
- {tsvz-3.35 → tsvz-3.37}/README.md +0 -0
- {tsvz-3.35 → tsvz-3.37}/TSVZ.egg-info/dependency_links.txt +0 -0
- {tsvz-3.35 → tsvz-3.37}/TSVZ.egg-info/entry_points.txt +0 -0
- {tsvz-3.35 → tsvz-3.37}/TSVZ.egg-info/top_level.txt +0 -0
- {tsvz-3.35 → tsvz-3.37}/setup.cfg +0 -0
{tsvz-3.35 → tsvz-3.37}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TSVZ
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.37
|
|
4
4
|
Summary: An simple in memory wrapper around a TSV file to function as a database
|
|
5
5
|
Home-page: https://github.com/yufei-pan/TSVZ
|
|
6
6
|
Author: Yufei Pan
|
|
@@ -11,6 +11,9 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
|
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Requires-Python: >=3.6
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: argparse
|
|
15
|
+
Provides-Extra: completion
|
|
16
|
+
Requires-Dist: argcomplete; extra == "completion"
|
|
14
17
|
Dynamic: author
|
|
15
18
|
Dynamic: author-email
|
|
16
19
|
Dynamic: classifier
|
|
@@ -18,6 +21,8 @@ Dynamic: description
|
|
|
18
21
|
Dynamic: description-content-type
|
|
19
22
|
Dynamic: home-page
|
|
20
23
|
Dynamic: license
|
|
24
|
+
Dynamic: provides-extra
|
|
25
|
+
Dynamic: requires-dist
|
|
21
26
|
Dynamic: requires-python
|
|
22
27
|
Dynamic: summary
|
|
23
28
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TSVZ
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.37
|
|
4
4
|
Summary: An simple in memory wrapper around a TSV file to function as a database
|
|
5
5
|
Home-page: https://github.com/yufei-pan/TSVZ
|
|
6
6
|
Author: Yufei Pan
|
|
@@ -11,6 +11,9 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
|
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Requires-Python: >=3.6
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: argparse
|
|
15
|
+
Provides-Extra: completion
|
|
16
|
+
Requires-Dist: argcomplete; extra == "completion"
|
|
14
17
|
Dynamic: author
|
|
15
18
|
Dynamic: author-email
|
|
16
19
|
Dynamic: classifier
|
|
@@ -18,6 +21,8 @@ Dynamic: description
|
|
|
18
21
|
Dynamic: description-content-type
|
|
19
22
|
Dynamic: home-page
|
|
20
23
|
Dynamic: license
|
|
24
|
+
Dynamic: provides-extra
|
|
25
|
+
Dynamic: requires-dist
|
|
21
26
|
Dynamic: requires-python
|
|
22
27
|
Dynamic: summary
|
|
23
28
|
|
{tsvz-3.35 → tsvz-3.37}/TSVZ.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
# PYTHON_ARGCOMPLETE_OK
|
|
2
3
|
# /// script
|
|
3
4
|
# requires-python = ">=3.6"
|
|
4
5
|
# dependencies = [
|
|
@@ -26,15 +27,15 @@ if os.name == 'nt':
|
|
|
26
27
|
elif os.name == 'posix':
|
|
27
28
|
import fcntl
|
|
28
29
|
|
|
29
|
-
version = '3.
|
|
30
|
+
version = '3.37'
|
|
30
31
|
__version__ = version
|
|
31
32
|
author = 'pan@zopyr.us'
|
|
32
|
-
COMMIT_DATE = '
|
|
33
|
+
COMMIT_DATE = '2026-04-21'
|
|
33
34
|
|
|
34
35
|
DEFAULT_DELIMITER = '\t'
|
|
35
36
|
DEFAULTS_INDICATOR_KEY = '#_defaults_#'
|
|
36
37
|
|
|
37
|
-
COMPRESSED_FILE_EXTENSIONS = ['gz','gzip','bz2','bzip2','xz','lzma']
|
|
38
|
+
COMPRESSED_FILE_EXTENSIONS = ['gz','gzip','bz2','bzip2','xz','lzma','zst','zstd']
|
|
38
39
|
|
|
39
40
|
def get_delimiter(delimiter,file_name = ''):
|
|
40
41
|
global DEFAULT_DELIMITER
|
|
@@ -75,34 +76,43 @@ def eprint(*args, **kwargs):
|
|
|
75
76
|
print(*args, **kwargs)
|
|
76
77
|
|
|
77
78
|
def openFileAsCompressed(fileName,mode = 'rb',encoding = 'utf8',teeLogger = None,compressLevel = 1):
|
|
79
|
+
lowerFileName = fileName.lower()
|
|
78
80
|
if 'b' not in mode:
|
|
79
81
|
mode += 't'
|
|
80
82
|
kwargs = {}
|
|
81
83
|
if 'r' not in mode:
|
|
82
|
-
if
|
|
84
|
+
if lowerFileName.endswith('.xz'):
|
|
83
85
|
kwargs['preset'] = compressLevel
|
|
86
|
+
elif lowerFileName.endswith('.zst') or lowerFileName.endswith('.zstd'):
|
|
87
|
+
kwargs['level'] = compressLevel
|
|
84
88
|
else:
|
|
85
89
|
kwargs['compresslevel'] = compressLevel
|
|
86
90
|
if 'b' not in mode:
|
|
87
91
|
kwargs['encoding'] = encoding
|
|
88
|
-
if
|
|
92
|
+
if lowerFileName.endswith('.xz') or lowerFileName.endswith('.lzma'):
|
|
89
93
|
try:
|
|
90
94
|
import lzma
|
|
91
95
|
return lzma.open(fileName, mode, **kwargs)
|
|
92
96
|
except Exception:
|
|
93
97
|
__teePrintOrNot(f"Failed to open {fileName} with lzma, trying bin",teeLogger=teeLogger)
|
|
94
|
-
elif
|
|
98
|
+
elif lowerFileName.endswith('.gz') or lowerFileName.endswith('.gzip'):
|
|
95
99
|
try:
|
|
96
100
|
import gzip
|
|
97
101
|
return gzip.open(fileName, mode, **kwargs)
|
|
98
102
|
except Exception:
|
|
99
103
|
__teePrintOrNot(f"Failed to open {fileName} with gzip, trying bin",teeLogger=teeLogger)
|
|
100
|
-
elif
|
|
104
|
+
elif lowerFileName.endswith('.bz2') or lowerFileName.endswith('.bzip2'):
|
|
101
105
|
try:
|
|
102
106
|
import bz2
|
|
103
107
|
return bz2.open(fileName, mode, **kwargs)
|
|
104
108
|
except Exception:
|
|
105
109
|
__teePrintOrNot(f"Failed to open {fileName} with bz2, trying bin",teeLogger=teeLogger)
|
|
110
|
+
elif lowerFileName.endswith('.zst') or lowerFileName.endswith('.zstd'):
|
|
111
|
+
try:
|
|
112
|
+
from compression import zstd
|
|
113
|
+
return zstd.open(fileName, mode, **kwargs)
|
|
114
|
+
except Exception:
|
|
115
|
+
__teePrintOrNot(f"Failed to open {fileName} with zstd, trying bin",teeLogger=teeLogger)
|
|
106
116
|
if 't' in mode:
|
|
107
117
|
mode = mode.replace('t','')
|
|
108
118
|
return open(fileName, mode, encoding=encoding)
|
|
@@ -545,8 +555,9 @@ def read_last_valid_line(fileName, taskDic, correctColumnNum, verbose=False, tee
|
|
|
545
555
|
)
|
|
546
556
|
# If the line is valid, return it
|
|
547
557
|
if lineCache:
|
|
548
|
-
if storeOffset
|
|
549
|
-
return
|
|
558
|
+
if storeOffset :
|
|
559
|
+
return file_size - processedSize + 1
|
|
560
|
+
return lineCache
|
|
550
561
|
|
|
551
562
|
# Keep the last (possibly incomplete) line in buffer for the next read
|
|
552
563
|
buffer = lines[0]
|
|
@@ -2260,6 +2271,11 @@ def __main__():
|
|
|
2260
2271
|
strictMode.add_argument('-f', '--force', dest = 'strict',action='store_false', help='Force the operation. Ignore checks for column numbers / headers')
|
|
2261
2272
|
parser.add_argument('-v', '--verbose', action='store_true', help='Print verbose output')
|
|
2262
2273
|
parser.add_argument('-V', '--version', action='version', version=f'%(prog)s {version} @ {COMMIT_DATE} by {author}')
|
|
2274
|
+
try:
|
|
2275
|
+
import argcomplete
|
|
2276
|
+
argcomplete.autocomplete(parser,always_complete_options='long')
|
|
2277
|
+
except ImportError:
|
|
2278
|
+
pass
|
|
2263
2279
|
args = parser.parse_args()
|
|
2264
2280
|
args.delimiter = get_delimiter(delimiter=args.delimiter,file_name=args.filename)
|
|
2265
2281
|
if args.header and args.header.endswith('\\'):
|
{tsvz-3.35 → tsvz-3.37}/setup.py
RENAMED
|
@@ -15,6 +15,14 @@ setup(
|
|
|
15
15
|
'tsvz = TSVZ:__main__',
|
|
16
16
|
],
|
|
17
17
|
},
|
|
18
|
+
install_requires=[
|
|
19
|
+
'argparse',
|
|
20
|
+
],
|
|
21
|
+
extras_require={
|
|
22
|
+
'completion': [
|
|
23
|
+
'argcomplete',
|
|
24
|
+
],
|
|
25
|
+
},
|
|
18
26
|
classifiers=[
|
|
19
27
|
'Programming Language :: Python :: 3',
|
|
20
28
|
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import tempfile
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
7
|
+
import TSVZ
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestTSVZRegressions(unittest.TestCase):
|
|
11
|
+
def test_append_and_read_preserves_embedded_tab_and_newline(self):
|
|
12
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
13
|
+
path = os.path.join(temp_dir, "embedded.tsv")
|
|
14
|
+
header = ["key", "payload"]
|
|
15
|
+
row = ["row1", "hello\tworld\nline2"]
|
|
16
|
+
|
|
17
|
+
TSVZ.appendTabularFile(
|
|
18
|
+
path,
|
|
19
|
+
row,
|
|
20
|
+
header=header,
|
|
21
|
+
createIfNotExist=True,
|
|
22
|
+
verifyHeader=True,
|
|
23
|
+
strict=True,
|
|
24
|
+
delimiter="\t",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
data = TSVZ.readTabularFile(
|
|
28
|
+
path,
|
|
29
|
+
header=header,
|
|
30
|
+
verifyHeader=True,
|
|
31
|
+
strict=True,
|
|
32
|
+
delimiter="\t",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
self.assertIn("row1", data)
|
|
36
|
+
self.assertEqual(data["row1"], row)
|
|
37
|
+
|
|
38
|
+
def test_read_uses_defaults_row_for_missing_values(self):
|
|
39
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
40
|
+
path = os.path.join(temp_dir, "defaults.tsv")
|
|
41
|
+
with open(path, "w", encoding="utf8") as handle:
|
|
42
|
+
handle.write("key\tcpu\tmem\n")
|
|
43
|
+
handle.write(f"{TSVZ.DEFAULTS_INDICATOR_KEY}\t4\t8G\n")
|
|
44
|
+
handle.write("job1\t2\t16G\n")
|
|
45
|
+
handle.write("job2\t\t32G\n")
|
|
46
|
+
|
|
47
|
+
data = TSVZ.readTabularFile(
|
|
48
|
+
path,
|
|
49
|
+
header=["key", "cpu", "mem"],
|
|
50
|
+
verifyHeader=True,
|
|
51
|
+
strict=True,
|
|
52
|
+
delimiter="\t",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.assertEqual(data["job1"], ["job1", "2", "16G"])
|
|
56
|
+
self.assertEqual(data["job2"], ["job2", "4", "32G"])
|
|
57
|
+
|
|
58
|
+
def test_last_line_only_ignores_delete_marker_line(self):
|
|
59
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
60
|
+
path = os.path.join(temp_dir, "lastline.tsv")
|
|
61
|
+
with open(path, "w", encoding="utf8") as handle:
|
|
62
|
+
handle.write("key\tvalue\n")
|
|
63
|
+
handle.write("alive\t42\n")
|
|
64
|
+
handle.write("alive\n")
|
|
65
|
+
|
|
66
|
+
last_valid = TSVZ.readTabularFile(
|
|
67
|
+
path,
|
|
68
|
+
header=["key", "value"],
|
|
69
|
+
verifyHeader=True,
|
|
70
|
+
lastLineOnly=True,
|
|
71
|
+
strict=False,
|
|
72
|
+
delimiter="\t",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.assertEqual(last_valid, ["alive", "42"])
|
|
76
|
+
|
|
77
|
+
def test_append_and_read_gzip_file(self):
|
|
78
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
79
|
+
path = os.path.join(temp_dir, "compressed.tsv.gz")
|
|
80
|
+
header = ["key", "value"]
|
|
81
|
+
|
|
82
|
+
TSVZ.appendTabularFile(
|
|
83
|
+
path,
|
|
84
|
+
["gk", "gv"],
|
|
85
|
+
header=header,
|
|
86
|
+
createIfNotExist=True,
|
|
87
|
+
verifyHeader=True,
|
|
88
|
+
strict=True,
|
|
89
|
+
delimiter="\t",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
data = TSVZ.readTabularFile(
|
|
93
|
+
path,
|
|
94
|
+
header=header,
|
|
95
|
+
verifyHeader=True,
|
|
96
|
+
strict=True,
|
|
97
|
+
delimiter="\t",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.assertEqual(data["gk"], ["gk", "gv"])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|