1
1
"""Text Utilities."""
2
-
3
2
# flake8: noqa
4
3
5
4
6
5
from __future__ import annotations
7
6
8
- import re
9
7
from difflib import SequenceMatcher
10
8
from typing import Iterable , Iterator
11
9
@@ -16,15 +14,12 @@ def escape_regex(p, white=''):
16
14
# type: (str, str) -> str
17
15
"""Escape string for use within a regular expression."""
18
16
# what's up with re.escape? that code must be neglected or something
19
- return '' .join (
20
- c if c .isalnum () or c in white else ('\\ 000' if c == '\000 ' else '\\ ' + c )
21
- for c in p
22
- )
17
+ return '' .join (c if c .isalnum () or c in white
18
+ else ('\\ 000' if c == '\000 ' else '\\ ' + c )
19
+ for c in p )
23
20
24
21
25
- def fmatch_iter (
26
- needle : str , haystack : Iterable [str ], min_ratio : float = 0.6
27
- ) -> Iterator [tuple [float , str ]]:
22
+ def fmatch_iter (needle : str , haystack : Iterable [str ], min_ratio : float = 0.6 ) -> Iterator [tuple [float , str ]]:
28
23
"""Fuzzy match: iteratively.
29
24
30
25
Yields
@@ -37,76 +32,39 @@ def fmatch_iter(
37
32
yield ratio , key
38
33
39
34
40
- def fmatch_best (
41
- needle : str , haystack : Iterable [str ], min_ratio : float = 0.6
42
- ) -> str | None :
35
+ def fmatch_best (needle : str , haystack : Iterable [str ], min_ratio : float = 0.6 ) -> str | None :
43
36
"""Fuzzy match - Find best match (scalar)."""
44
37
try :
45
38
return sorted (
46
- fmatch_iter (needle , haystack , min_ratio ),
47
- reverse = True ,
48
- )[
49
- 0
50
- ][1 ]
39
+ fmatch_iter (needle , haystack , min_ratio ), reverse = True ,
40
+ )[0 ][1 ]
51
41
except IndexError :
52
42
return None
53
43
54
44
55
- def version_string_as_tuple (version : str ) -> version_info_t :
56
- """Parse a version string into its components and return a version_info_t tuple.
57
-
58
- The version string is expected to follow the pattern:
59
- 'major.minor.micro[releaselevel][serial]'. Each component of the version
60
- is extracted and returned as a tuple in the format (major, minor, micro,
61
- releaselevel, serial).
62
-
63
- Args
64
- ----
65
- version (str): The version string to parse.
66
-
67
- Returns
68
- -------
69
- version_info_t: A tuple containing the parsed version components.
70
-
71
- Raises
72
- ------
73
- ValueError: If the version string is invalid and does not match the expected pattern.
74
- """
75
- pattern = r'^(\d+)' # catching the major version (mandatory)
76
- pattern += r'(?:\.(\d+))?' # optionally catching the minor version
77
- pattern += r'(?:\.(\d+))?' # optionally catching the micro version
78
- pattern += r'(?:\.*([a-zA-Z+-][\da-zA-Z+-]*))?' # optionally catching the release level (starting with a letter, + or -) after a dot
79
- pattern += r'(?:\.(.*))?' # optionally catching the serial number after a dot
80
-
81
- # applying the regex pattern to the input version string
82
- match = re .match (pattern , version )
83
-
84
- if not match :
85
- raise ValueError (f"Invalid version string: { version } " )
86
-
87
- # extracting the matched groups
88
- major = int (match .group (1 ))
89
- minor = int (match .group (2 )) if match .group (2 ) else 0
90
- micro = int (match .group (3 )) if match .group (3 ) else 0
91
- releaselevel = match .group (4 ) if match .group (4 ) else ''
92
- serial = match .group (5 ) if match .group (5 ) else ''
93
-
94
- return _unpack_version (major , minor , micro , releaselevel , serial )
45
+ def version_string_as_tuple (s : str ) -> version_info_t :
46
+ """Convert version string to version info tuple."""
47
+ v = _unpack_version (* s .split ('.' ))
48
+ # X.Y.3a1 -> (X, Y, 3, 'a1')
49
+ if isinstance (v .micro , str ):
50
+ v = version_info_t (v .major , v .minor , * _splitmicro (* v [2 :]))
51
+ # X.Y.3a1-40 -> (X, Y, 3, 'a1', '40')
52
+ if not v .serial and v .releaselevel and '-' in v .releaselevel :
53
+ v = version_info_t (* list (v [0 :3 ]) + v .releaselevel .split ('-' ))
54
+ return v
95
55
96
56
97
57
def _unpack_version (
98
- major : str | int = 0 ,
58
+ major : str ,
99
59
minor : str | int = 0 ,
100
60
micro : str | int = 0 ,
101
61
releaselevel : str = '' ,
102
- serial : str = '' ,
62
+ serial : str = ''
103
63
) -> version_info_t :
104
- return version_info_t (int (major ), int (minor ), int ( micro ) , releaselevel , serial )
64
+ return version_info_t (int (major ), int (minor ), micro , releaselevel , serial )
105
65
106
66
107
- def _splitmicro (
108
- micro : str , releaselevel : str = '' , serial : str = ''
109
- ) -> tuple [int , str , str ]:
67
+ def _splitmicro (micro : str , releaselevel : str = '' , serial : str = '' ) -> tuple [int , str , str ]:
110
68
for index , char in enumerate (micro ):
111
69
if not char .isdigit ():
112
70
break
0 commit comments