TSVZ 3.37__tar.gz → 3.38__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.37 → tsvz-3.38}/PKG-INFO +1 -1
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/PKG-INFO +1 -1
- {tsvz-3.37 → tsvz-3.38}/TSVZ.py +111 -33
- {tsvz-3.37 → tsvz-3.38}/README.md +0 -0
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/SOURCES.txt +0 -0
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/dependency_links.txt +0 -0
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/entry_points.txt +0 -0
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/requires.txt +0 -0
- {tsvz-3.37 → tsvz-3.38}/TSVZ.egg-info/top_level.txt +0 -0
- {tsvz-3.37 → tsvz-3.38}/setup.cfg +0 -0
- {tsvz-3.37 → tsvz-3.38}/setup.py +0 -0
- {tsvz-3.37 → tsvz-3.38}/tests/test_regression.py +0 -0
{tsvz-3.37 → tsvz-3.38}/PKG-INFO
RENAMED
{tsvz-3.37 → tsvz-3.38}/TSVZ.py
RENAMED
|
@@ -10,7 +10,6 @@ import functools
|
|
|
10
10
|
import io
|
|
11
11
|
import os
|
|
12
12
|
import re
|
|
13
|
-
from tabnanny import verbose
|
|
14
13
|
import threading
|
|
15
14
|
import time
|
|
16
15
|
import sys
|
|
@@ -27,23 +26,42 @@ if os.name == 'nt':
|
|
|
27
26
|
elif os.name == 'posix':
|
|
28
27
|
import fcntl
|
|
29
28
|
|
|
30
|
-
version = '3.
|
|
29
|
+
version = '3.38'
|
|
31
30
|
__version__ = version
|
|
32
31
|
author = 'pan@zopyr.us'
|
|
33
|
-
COMMIT_DATE = '2026-04
|
|
32
|
+
COMMIT_DATE = '2026-06-04'
|
|
34
33
|
|
|
35
34
|
DEFAULT_DELIMITER = '\t'
|
|
36
35
|
DEFAULTS_INDICATOR_KEY = '#_defaults_#'
|
|
37
36
|
|
|
38
37
|
COMPRESSED_FILE_EXTENSIONS = ['gz','gzip','bz2','bzip2','xz','lzma','zst','zstd']
|
|
39
38
|
|
|
40
|
-
def
|
|
39
|
+
def _isCompressedFile(fileName):
|
|
40
|
+
return fileName.rpartition('.')[2].lower() in COMPRESSED_FILE_EXTENSIONS
|
|
41
|
+
|
|
42
|
+
def get_delimiter(delimiter = ...,file_name = ''):
|
|
43
|
+
"""
|
|
44
|
+
Resolve the delimiter to use for an operation.
|
|
45
|
+
|
|
46
|
+
When the delimiter is not provided (the ``...`` sentinel or a falsy value),
|
|
47
|
+
it is determined automatically: inferred from the file name when one is
|
|
48
|
+
available, otherwise falling back to the last-determined default. Explicit
|
|
49
|
+
delimiters (including the aliases 'comma', 'tab', 'pipe', 'null' and escape
|
|
50
|
+
sequences) are normalized. The resolved value is remembered as the new
|
|
51
|
+
module-level default.
|
|
52
|
+
|
|
53
|
+
Parameters:
|
|
54
|
+
- delimiter: The delimiter or alias, or ``...``/``None`` to auto-determine.
|
|
55
|
+
- file_name (str, optional): The file name to infer the delimiter from.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: The resolved delimiter.
|
|
59
|
+
"""
|
|
41
60
|
global DEFAULT_DELIMITER
|
|
42
|
-
if
|
|
43
|
-
return DEFAULT_DELIMITER
|
|
44
|
-
elif delimiter == ...:
|
|
61
|
+
if delimiter is ...:
|
|
45
62
|
if not file_name:
|
|
46
|
-
|
|
63
|
+
# Nothing to infer from; reuse the last-determined default.
|
|
64
|
+
return DEFAULT_DELIMITER
|
|
47
65
|
elif file_name.endswith('.csv'):
|
|
48
66
|
rtn = ','
|
|
49
67
|
elif file_name.endswith('.nsv'):
|
|
@@ -52,6 +70,8 @@ def get_delimiter(delimiter,file_name = ''):
|
|
|
52
70
|
rtn = '|'
|
|
53
71
|
else:
|
|
54
72
|
rtn = '\t'
|
|
73
|
+
elif not delimiter:
|
|
74
|
+
return DEFAULT_DELIMITER
|
|
55
75
|
elif delimiter == 'comma':
|
|
56
76
|
rtn = ','
|
|
57
77
|
elif delimiter == 'tab':
|
|
@@ -338,7 +358,7 @@ def format_bytes(size, use_1024_bytes=None, to_int=False, to_str=False,str_forma
|
|
|
338
358
|
power = 2**10
|
|
339
359
|
n = 0
|
|
340
360
|
power_labels = {0 : '', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti', 5: 'Pi'}
|
|
341
|
-
while size
|
|
361
|
+
while size >= power:
|
|
342
362
|
size /= power
|
|
343
363
|
n += 1
|
|
344
364
|
return f"{size:{str_format}}{' '}{power_labels[n]}"
|
|
@@ -346,7 +366,7 @@ def format_bytes(size, use_1024_bytes=None, to_int=False, to_str=False,str_forma
|
|
|
346
366
|
power = 10**3
|
|
347
367
|
n = 0
|
|
348
368
|
power_labels = {0 : '', 1: 'K', 2: 'M', 3: 'G', 4: 'T', 5: 'P'}
|
|
349
|
-
while size
|
|
369
|
+
while size >= power:
|
|
350
370
|
size /= power
|
|
351
371
|
n += 1
|
|
352
372
|
return f"{size:{str_format}}{' '}{power_labels[n]}"
|
|
@@ -413,7 +433,7 @@ def __teePrintOrNot(message,level = 'info',teeLogger = None):
|
|
|
413
433
|
except Exception:
|
|
414
434
|
print(message,flush=True)
|
|
415
435
|
|
|
416
|
-
def _processLine(line,taskDic,correctColumnNum,strict = True,delimiter =
|
|
436
|
+
def _processLine(line,taskDic,correctColumnNum,strict = True,delimiter = ...,defaults = ...,
|
|
417
437
|
storeOffset = False, offset = -1):
|
|
418
438
|
"""
|
|
419
439
|
Process a line of text and update the task dictionary.
|
|
@@ -433,6 +453,7 @@ def _processLine(line,taskDic,correctColumnNum,strict = True,delimiter = DEFAULT
|
|
|
433
453
|
"""
|
|
434
454
|
if defaults is ...:
|
|
435
455
|
defaults = []
|
|
456
|
+
delimiter = get_delimiter(delimiter)
|
|
436
457
|
line = line.strip('\x00').rstrip('\r\n')
|
|
437
458
|
if not line or (line.startswith('#') and not line.startswith(DEFAULTS_INDICATOR_KEY)):
|
|
438
459
|
# if verbose:
|
|
@@ -456,7 +477,7 @@ def _processLine(line,taskDic,correctColumnNum,strict = True,delimiter = DEFAULT
|
|
|
456
477
|
# if verbose:
|
|
457
478
|
# __teePrintOrNot(f"Empty defaults line found: {line}",teeLogger=teeLogger)
|
|
458
479
|
defaults.clear()
|
|
459
|
-
defaults[
|
|
480
|
+
defaults[:] = [DEFAULTS_INDICATOR_KEY]
|
|
460
481
|
else:
|
|
461
482
|
# if verbose:
|
|
462
483
|
# __teePrintOrNot(f"Key {lineCache[0]} found with empty value, deleting such key's representaion",teeLogger=teeLogger)
|
|
@@ -491,6 +512,44 @@ def _processLine(line,taskDic,correctColumnNum,strict = True,delimiter = DEFAULT
|
|
|
491
512
|
# __teePrintOrNot(f"Key {lineCache[0]} added",teeLogger=teeLogger)
|
|
492
513
|
return correctColumnNum, lineCache
|
|
493
514
|
|
|
515
|
+
def _read_last_valid_line_forward(fileName, taskDic, correctColumnNum, verbose=False, teeLogger=None,
|
|
516
|
+
strict=False, encoding='utf8', delimiter=...,
|
|
517
|
+
defaults=None, storeOffset=False):
|
|
518
|
+
"""
|
|
519
|
+
Forward, single-pass variant of read_last_valid_line for compressed files.
|
|
520
|
+
|
|
521
|
+
Backward chunked seeking forces a full re-decompress on every seek (and is not
|
|
522
|
+
reliably supported for some codecs e.g. zstd), so for compressed files we decompress
|
|
523
|
+
once, streaming forward, and keep only the last valid line / offset. A scratch dict is
|
|
524
|
+
used so memory stays flat instead of accumulating the whole file into taskDic.
|
|
525
|
+
"""
|
|
526
|
+
if defaults is None:
|
|
527
|
+
defaults = []
|
|
528
|
+
delimiter = get_delimiter(delimiter, file_name=fileName)
|
|
529
|
+
last_valid = -1 if storeOffset else []
|
|
530
|
+
scratch = {}
|
|
531
|
+
with openFileAsCompressed(fileName, 'rb', encoding=encoding, teeLogger=teeLogger) as file:
|
|
532
|
+
while True:
|
|
533
|
+
offset = file.tell()
|
|
534
|
+
line = file.readline()
|
|
535
|
+
if not line:
|
|
536
|
+
break
|
|
537
|
+
if not line.strip():
|
|
538
|
+
continue
|
|
539
|
+
correctColumnNum, lineCache = _processLine(
|
|
540
|
+
line=line.decode(encoding=encoding, errors='replace'),
|
|
541
|
+
taskDic=scratch,
|
|
542
|
+
correctColumnNum=correctColumnNum,
|
|
543
|
+
strict=strict,
|
|
544
|
+
delimiter=delimiter,
|
|
545
|
+
defaults=defaults,
|
|
546
|
+
storeOffset=storeOffset,
|
|
547
|
+
offset=offset,
|
|
548
|
+
)
|
|
549
|
+
if lineCache:
|
|
550
|
+
last_valid = offset if storeOffset else lineCache
|
|
551
|
+
return last_valid
|
|
552
|
+
|
|
494
553
|
def read_last_valid_line(fileName, taskDic, correctColumnNum, verbose=False, teeLogger=None, strict=False,
|
|
495
554
|
encoding = 'utf8',delimiter = ...,defaults = ...,storeOffset = False ):
|
|
496
555
|
"""
|
|
@@ -518,6 +577,12 @@ def read_last_valid_line(fileName, taskDic, correctColumnNum, verbose=False, tee
|
|
|
518
577
|
delimiter = get_delimiter(delimiter,file_name=fileName)
|
|
519
578
|
if verbose:
|
|
520
579
|
__teePrintOrNot(f"Reading last line only from {fileName}",teeLogger=teeLogger)
|
|
580
|
+
if _isCompressedFile(fileName):
|
|
581
|
+
# Backward seeking through a codec is slow/unsupported; decompress once, forward.
|
|
582
|
+
return _read_last_valid_line_forward(
|
|
583
|
+
fileName, taskDic, correctColumnNum, verbose=verbose, teeLogger=teeLogger,
|
|
584
|
+
strict=strict, encoding=encoding, delimiter=delimiter, defaults=defaults,
|
|
585
|
+
storeOffset=storeOffset)
|
|
521
586
|
with openFileAsCompressed(fileName, 'rb',encoding=encoding, teeLogger=teeLogger) as file:
|
|
522
587
|
file.seek(0, os.SEEK_END)
|
|
523
588
|
file_size = file.tell()
|
|
@@ -568,7 +633,8 @@ def read_last_valid_line(fileName, taskDic, correctColumnNum, verbose=False, tee
|
|
|
568
633
|
return last_valid_line
|
|
569
634
|
|
|
570
635
|
@functools.lru_cache(maxsize=None)
|
|
571
|
-
def _get_sanitization_re(delimiter =
|
|
636
|
+
def _get_sanitization_re(delimiter = ...):
|
|
637
|
+
delimiter = get_delimiter(delimiter)
|
|
572
638
|
return re.compile(r"(</sep/>|</LF/>|<sep>|<LF>|\n|" + re.escape(delimiter) + r")")
|
|
573
639
|
|
|
574
640
|
_sanitize_replacements = {
|
|
@@ -578,9 +644,10 @@ _sanitize_replacements = {
|
|
|
578
644
|
}
|
|
579
645
|
_inverse_sanitize_replacements = {v: k for k, v in _sanitize_replacements.items()}
|
|
580
646
|
|
|
581
|
-
def _sanitize(data,delimiter =
|
|
647
|
+
def _sanitize(data,delimiter = ...):
|
|
582
648
|
if not data:
|
|
583
649
|
return data
|
|
650
|
+
delimiter = get_delimiter(delimiter)
|
|
584
651
|
def repl(m):
|
|
585
652
|
tok = m.group(0)
|
|
586
653
|
if tok == delimiter:
|
|
@@ -594,9 +661,10 @@ def _sanitize(data,delimiter = DEFAULT_DELIMITER):
|
|
|
594
661
|
else:
|
|
595
662
|
return [pattern.sub(repl,str(segment)) if segment else '' for segment in data]
|
|
596
663
|
|
|
597
|
-
def _unsanitize(data,delimiter =
|
|
664
|
+
def _unsanitize(data,delimiter = ...):
|
|
598
665
|
if not data:
|
|
599
666
|
return data
|
|
667
|
+
delimiter = get_delimiter(delimiter)
|
|
600
668
|
def repl(m):
|
|
601
669
|
tok = m.group(0)
|
|
602
670
|
if tok == "<sep>":
|
|
@@ -608,7 +676,7 @@ def _unsanitize(data,delimiter = DEFAULT_DELIMITER):
|
|
|
608
676
|
else:
|
|
609
677
|
return [pattern.sub(repl,str(segment).rstrip()) if segment else '' for segment in data]
|
|
610
678
|
|
|
611
|
-
def _formatHeader(header,verbose = False,teeLogger = None,delimiter =
|
|
679
|
+
def _formatHeader(header,verbose = False,teeLogger = None,delimiter = ...):
|
|
612
680
|
"""
|
|
613
681
|
Format the header string.
|
|
614
682
|
|
|
@@ -621,7 +689,7 @@ def _formatHeader(header,verbose = False,teeLogger = None,delimiter = DEFAULT_DE
|
|
|
621
689
|
list: The formatted header list of string.
|
|
622
690
|
"""
|
|
623
691
|
if isinstance(header,str):
|
|
624
|
-
header = header.split(delimiter)
|
|
692
|
+
header = header.split(get_delimiter(delimiter))
|
|
625
693
|
else:
|
|
626
694
|
try:
|
|
627
695
|
header = [str(s) for s in header]
|
|
@@ -631,7 +699,7 @@ def _formatHeader(header,verbose = False,teeLogger = None,delimiter = DEFAULT_DE
|
|
|
631
699
|
header = []
|
|
632
700
|
return [s.rstrip() for s in header]
|
|
633
701
|
|
|
634
|
-
def _lineContainHeader(header,line,verbose = False,teeLogger = None,strict = False,delimiter =
|
|
702
|
+
def _lineContainHeader(header,line,verbose = False,teeLogger = None,strict = False,delimiter = ...):
|
|
635
703
|
"""
|
|
636
704
|
Verify if a line contains the header.
|
|
637
705
|
|
|
@@ -645,6 +713,7 @@ def _lineContainHeader(header,line,verbose = False,teeLogger = None,strict = Fal
|
|
|
645
713
|
Returns:
|
|
646
714
|
bool: True if the header matches the line, False otherwise.
|
|
647
715
|
"""
|
|
716
|
+
delimiter = get_delimiter(delimiter)
|
|
648
717
|
line = _formatHeader(line,verbose=verbose,teeLogger=teeLogger,delimiter=delimiter)
|
|
649
718
|
if verbose:
|
|
650
719
|
__teePrintOrNot(f"Header: \n{header}",teeLogger=teeLogger)
|
|
@@ -656,7 +725,7 @@ def _lineContainHeader(header,line,verbose = False,teeLogger = None,strict = Fal
|
|
|
656
725
|
return False
|
|
657
726
|
return True
|
|
658
727
|
|
|
659
|
-
def _verifyFileExistence(fileName,createIfNotExist = True,teeLogger = None,header = [],encoding = 'utf8',strict = True,delimiter =
|
|
728
|
+
def _verifyFileExistence(fileName,createIfNotExist = True,teeLogger = None,header = [],encoding = 'utf8',strict = True,delimiter = ...):
|
|
660
729
|
"""
|
|
661
730
|
Verify the existence of the tabular file.
|
|
662
731
|
|
|
@@ -671,6 +740,7 @@ def _verifyFileExistence(fileName,createIfNotExist = True,teeLogger = None,heade
|
|
|
671
740
|
Returns:
|
|
672
741
|
bool: True if the file exists, False otherwise.
|
|
673
742
|
"""
|
|
743
|
+
delimiter = get_delimiter(delimiter, file_name=fileName)
|
|
674
744
|
remainingFileName, _ ,extenstionName = fileName.rpartition('.')
|
|
675
745
|
if extenstionName in COMPRESSED_FILE_EXTENSIONS:
|
|
676
746
|
remainingFileName, _ ,extenstionName = remainingFileName.rpartition('.')
|
|
@@ -687,7 +757,6 @@ def _verifyFileExistence(fileName,createIfNotExist = True,teeLogger = None,heade
|
|
|
687
757
|
try:
|
|
688
758
|
with openFileAsCompressed(fileName, mode ='wb',encoding=encoding,teeLogger=teeLogger)as file:
|
|
689
759
|
header = delimiter.join(_sanitize(_formatHeader(header,
|
|
690
|
-
verbose=verbose,
|
|
691
760
|
teeLogger=teeLogger,
|
|
692
761
|
delimiter=delimiter,
|
|
693
762
|
),delimiter=delimiter))
|
|
@@ -780,7 +849,7 @@ def readTabularFile(fileName,teeLogger = None,header = '',createIfNotExist = Fal
|
|
|
780
849
|
with openFileAsCompressed(fileName, mode ='rb',encoding=encoding,teeLogger=teeLogger)as file:
|
|
781
850
|
if any(header) and verifyHeader:
|
|
782
851
|
line = file.readline().decode(encoding=encoding,errors='replace')
|
|
783
|
-
if _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict) and correctColumnNum == -1:
|
|
852
|
+
if _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict,delimiter = delimiter) and correctColumnNum == -1:
|
|
784
853
|
correctColumnNum = len(header)
|
|
785
854
|
if verbose:
|
|
786
855
|
__teePrintOrNot(f"correctColumnNum: {correctColumnNum}",teeLogger=teeLogger)
|
|
@@ -882,7 +951,7 @@ def appendLinesTabularFile(fileName,linesToAppend,teeLogger = None,header = '',c
|
|
|
882
951
|
if any(header) and verifyHeader:
|
|
883
952
|
with openFileAsCompressed(fileName, mode ='rb',encoding=encoding,teeLogger=teeLogger)as file:
|
|
884
953
|
line = file.readline().decode(encoding=encoding,errors='replace')
|
|
885
|
-
if _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict):
|
|
954
|
+
if _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict,delimiter = delimiter):
|
|
886
955
|
correctColumnNum = len(header)
|
|
887
956
|
if verbose:
|
|
888
957
|
__teePrintOrNot(f"correctColumnNum: {correctColumnNum}",teeLogger=teeLogger)
|
|
@@ -936,7 +1005,7 @@ def clearTabularFile(fileName,teeLogger = None,header = '',verifyHeader = False,
|
|
|
936
1005
|
with openFileAsCompressed(fileName, mode ='rb',encoding=encoding,teeLogger=teeLogger)as file:
|
|
937
1006
|
if any(header) and verifyHeader:
|
|
938
1007
|
line = file.readline().decode(encoding=encoding,errors='replace')
|
|
939
|
-
if not _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict):
|
|
1008
|
+
if not _lineContainHeader(header,line,verbose = verbose,teeLogger = teeLogger,strict = strict,delimiter = delimiter):
|
|
940
1009
|
__teePrintOrNot(f'Warning: Header mismatch in {fileName}. Keeping original header in file...','warning',teeLogger)
|
|
941
1010
|
header = _formatHeader(line,verbose = verbose,teeLogger = teeLogger,delimiter=delimiter)
|
|
942
1011
|
with openFileAsCompressed(fileName, mode ='wb',encoding=encoding,teeLogger=teeLogger)as file:
|
|
@@ -1029,10 +1098,10 @@ def scrubTabularFile(fileName,teeLogger = None,header = '',createIfNotExist = Fa
|
|
|
1029
1098
|
appendLinesTabularFile(fileName,file,teeLogger = teeLogger,header = header,createIfNotExist = createIfNotExist,verifyHeader = verifyHeader,verbose = verbose,encoding = encoding,strict = strict,delimiter = delimiter)
|
|
1030
1099
|
return file
|
|
1031
1100
|
|
|
1032
|
-
def getListView(tsvzDic,header = [],delimiter =
|
|
1101
|
+
def getListView(tsvzDic,header = [],delimiter = ...):
|
|
1033
1102
|
if header:
|
|
1034
1103
|
if isinstance(header,str):
|
|
1035
|
-
header = header.split(delimiter)
|
|
1104
|
+
header = header.split(get_delimiter(delimiter))
|
|
1036
1105
|
elif not isinstance(header,list):
|
|
1037
1106
|
try:
|
|
1038
1107
|
header = list(header)
|
|
@@ -1076,7 +1145,7 @@ class TSVZed(OrderedDict):
|
|
|
1076
1145
|
rewrite_on_exit : bool, default=False
|
|
1077
1146
|
If True, rewrites the entire file when closing/exiting.
|
|
1078
1147
|
rewrite_interval : float, default=0
|
|
1079
|
-
Minimum time interval (in seconds) between full file rewrites. 0 means
|
|
1148
|
+
Minimum time interval (in seconds) between full file rewrites. 0 means do not rewrite.
|
|
1080
1149
|
append_check_delay : float, default=0.01
|
|
1081
1150
|
Time delay (in seconds) between checks of the append queue by the worker thread.
|
|
1082
1151
|
monitor_external_changes : bool, default=True
|
|
@@ -1605,7 +1674,7 @@ memoryOnly:{self.memoryOnly}
|
|
|
1605
1674
|
if self.header:
|
|
1606
1675
|
line = file.readline().decode(self.encoding,errors='replace')
|
|
1607
1676
|
aftPos = file.tell()
|
|
1608
|
-
if not _lineContainHeader(self.header,line,verbose = self.verbose,teeLogger = self.teeLogger,strict = self.strict):
|
|
1677
|
+
if not _lineContainHeader(self.header,line,verbose = self.verbose,teeLogger = self.teeLogger,strict = self.strict,delimiter = self.delimiter):
|
|
1609
1678
|
header = self.delimiter.join(_sanitize(self.header,delimiter=self.delimiter))
|
|
1610
1679
|
file.seek(0)
|
|
1611
1680
|
file.write(f'{header}\n'.encode(encoding=self.encoding,errors='replace'))
|
|
@@ -1736,6 +1805,7 @@ memoryOnly:{self.memoryOnly}
|
|
|
1736
1805
|
|
|
1737
1806
|
def get_file_obj(self,modes = 'ab'):
|
|
1738
1807
|
self.writeLock.acquire()
|
|
1808
|
+
file = None
|
|
1739
1809
|
try:
|
|
1740
1810
|
if not self.encoding:
|
|
1741
1811
|
self.encoding = 'utf8'
|
|
@@ -1762,6 +1832,8 @@ memoryOnly:{self.memoryOnly}
|
|
|
1762
1832
|
# if write lock is already released, return
|
|
1763
1833
|
if not self.writeLock.locked():
|
|
1764
1834
|
return
|
|
1835
|
+
if file is None:
|
|
1836
|
+
return
|
|
1765
1837
|
try:
|
|
1766
1838
|
file.flush() # Ensure the file is flushed before unlocking
|
|
1767
1839
|
os.fsync(file.fileno()) # Ensure the file is synced to disk before unlocking
|
|
@@ -2034,13 +2106,18 @@ class TSVZedLite(MutableMapping):
|
|
|
2034
2106
|
)
|
|
2035
2107
|
if self.verbose:
|
|
2036
2108
|
eprint(f"Read at position {pos}: {segments}")
|
|
2037
|
-
if key is not
|
|
2109
|
+
if key is not ...:
|
|
2110
|
+
if not segments:
|
|
2111
|
+
eprint(f"Error: No segments found at position {pos}")
|
|
2112
|
+
elif segments[0] != key:
|
|
2038
2113
|
eprint(f"Warning: Key mismatch at position {pos}: expected {key}, got {segments[0]}")
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2114
|
+
else:
|
|
2115
|
+
return segments
|
|
2116
|
+
if self.strict:
|
|
2117
|
+
eprint("Error: Key mismatch and strict mode enabled. Raising KeyError.")
|
|
2118
|
+
raise KeyError(key)
|
|
2119
|
+
else :
|
|
2120
|
+
eprint("Continuing despite key mismatch due to non-strict mode. Expect errors!")
|
|
2044
2121
|
return segments
|
|
2045
2122
|
|
|
2046
2123
|
# Implement basic __getitem__, __setitem__, __delitem__, __iter__, and __len__. needed for MutableMapping
|
|
@@ -2093,6 +2170,7 @@ class TSVZedLite(MutableMapping):
|
|
|
2093
2170
|
self.defaults = value
|
|
2094
2171
|
if self.verbose:
|
|
2095
2172
|
eprint(f"Defaults set to {value}")
|
|
2173
|
+
return
|
|
2096
2174
|
elif key.startswith('#'):
|
|
2097
2175
|
if self.verbose:
|
|
2098
2176
|
eprint(f"Key {key} updated in memory (data in index) as it starts with #")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tsvz-3.37 → tsvz-3.38}/setup.py
RENAMED
|
File without changes
|
|
File without changes
|