TSVZ 3.36__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.36 → tsvz-3.37}/PKG-INFO +6 -1
- {tsvz-3.36 → tsvz-3.37}/TSVZ.egg-info/PKG-INFO +6 -1
- {tsvz-3.36 → tsvz-3.37}/TSVZ.egg-info/SOURCES.txt +3 -1
- tsvz-3.37/TSVZ.egg-info/requires.txt +4 -0
- {tsvz-3.36 → tsvz-3.37}/TSVZ.py +23 -8
- {tsvz-3.36 → tsvz-3.37}/setup.py +8 -0
- tsvz-3.37/tests/test_regression.py +104 -0
- {tsvz-3.36 → tsvz-3.37}/README.md +0 -0
- {tsvz-3.36 → tsvz-3.37}/TSVZ.egg-info/dependency_links.txt +0 -0
- {tsvz-3.36 → tsvz-3.37}/TSVZ.egg-info/entry_points.txt +0 -0
- {tsvz-3.36 → tsvz-3.37}/TSVZ.egg-info/top_level.txt +0 -0
- {tsvz-3.36 → tsvz-3.37}/setup.cfg +0 -0
{tsvz-3.36 → 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.36 → 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 = '2026-
|
|
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)
|
|
@@ -2261,6 +2271,11 @@ def __main__():
|
|
|
2261
2271
|
strictMode.add_argument('-f', '--force', dest = 'strict',action='store_false', help='Force the operation. Ignore checks for column numbers / headers')
|
|
2262
2272
|
parser.add_argument('-v', '--verbose', action='store_true', help='Print verbose output')
|
|
2263
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
|
|
2264
2279
|
args = parser.parse_args()
|
|
2265
2280
|
args.delimiter = get_delimiter(delimiter=args.delimiter,file_name=args.filename)
|
|
2266
2281
|
if args.header and args.header.endswith('\\'):
|
{tsvz-3.36 → 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
|