Coverage for src/extratools_core/strtools.py: 58%

43 statements  

« prev     ^ index     » next       coverage.py v7.8.1, created at 2025-06-24 04:41 -0700

1import gzip 

2from base64 import b64decode, b64encode 

3from collections.abc import Callable, Iterable 

4from fnmatch import fnmatchcase 

5from typing import Literal 

6 

7import simple_zstd as zstd 

8 

9from .iter import iter_to_grams 

10from .seq.subseq import common_subseq, enumerate_subseqs 

11 

12 

13def str_to_grams( 

14 s: str, 

15 *, 

16 n: int, 

17 pad: str = '', 

18) -> Iterable[str]: 

19 if n < 1 or len(pad) > 1: 

20 raise ValueError 

21 

22 for c in iter_to_grams(s, n=n, pad=pad or None): 

23 yield ''.join(c) 

24 

25 

26def common_substr(a: str, b: str) -> str: 

27 return ''.join(common_subseq(a, b)) 

28 

29 

30def enumerate_substrs(s: str) -> Iterable[str]: 

31 return map(str, enumerate_subseqs(s)) 

32 

33 

34def compress( 

35 s: str, 

36 compress_func: Callable[[bytes], bytes] = gzip.compress, 

37) -> str: 

38 """ 

39 Compress a string by GZip + Base64 encoding. 

40 

41 https://base64.guru/developers/data-uri/gzip 

42 

43 Parameters 

44 ---------- 

45 s : str 

46 String to compress 

47 compress_func : Callable[[bytes], bytes] 

48 Optional function to compress (`gzip.compress` in default) 

49 

50 Returns 

51 ------- 

52 str 

53 Compressed string 

54 """ 

55 

56 return b64encode(compress_func(s.encode())).decode() 

57 

58 

59def encode( 

60 s: str, 

61 *, 

62 encoding: Literal["gzip", "zstd"] | None = None, 

63) -> str: 

64 match encoding: 

65 case "gzip": 

66 return compress(s, gzip.compress) 

67 case "zstd": 

68 return compress(s, zstd.compress) 

69 case None: 

70 return s 

71 case _: 

72 raise ValueError 

73 

74 

75def decompress( 

76 s: str, 

77 decompress_func: Callable[[bytes], bytes] = gzip.decompress, 

78) -> str: 

79 """ 

80 Decompress a string with GZip + Base64 encoding. 

81 

82 https://base64.guru/developers/data-uri/gzip 

83 

84 Parameters 

85 ---------- 

86 s : str 

87 String to decompress 

88 compress_func : Callable[[bytes], bytes] 

89 Optional function to decompress (`gzip.decompress` in default) 

90 

91 Returns 

92 ------- 

93 str 

94 Decompressed string 

95 """ 

96 

97 return decompress_func(b64decode(s.encode())).decode() 

98 

99 

100def decode( 

101 s: str, 

102 *, 

103 encoding: Literal["gzip", "zstd"] | None = None, 

104) -> str: 

105 match encoding: 

106 case "gzip": 

107 return decompress(s, gzip.decompress) 

108 case "zstd": 

109 return decompress(s, zstd.decompress) 

110 case None: 

111 return s 

112 case _: 

113 raise ValueError 

114 

115 

116def wildcard_match( 

117 key: str, 

118 *, 

119 includes: Iterable[str] | None = None, 

120 excludes: Iterable[str] | None = None, 

121) -> bool: 

122 return ( 

123 ( 

124 includes is None 

125 or any( 

126 fnmatchcase(key, include) 

127 for include in includes 

128 ) 

129 ) 

130 and ( 

131 excludes is None 

132 or not any( 

133 fnmatchcase(key, exclude) 

134 for exclude in excludes 

135 

136 ) 

137 ) 

138 )