| 102 | 102 | return version |
| 103 | 103 | |
| 104 | 104 | |
| 105 | def _regex_csv_split(value: str) -> Sequence[str]: | |
| 106 | """Split a comma-separated list of regular expressions, respecting commas within regex syntax. | |
| 107 | ||
| 108 | This function handles cases where commas appear within regex quantifiers like {1,3} | |
| 109 | or within quoted patterns, treating them as part of the regex rather than delimiters. | |
| 110 | """ | |
| 111 | if isinstance(value, (list, tuple)): | |
| 112 | return [str(v) for v in value] | |
| 113 | ||
| 114 | patterns: list[str] = [] | |
| 115 | current_pattern = [] | |
| 116 | i = 0 | |
| 117 | n = len(value) | |
| 118 | ||
| 119 | while i < n: | |
| 120 | char = value[i] | |
| 121 | ||
| 122 | if char == ',' and _is_delimiter_comma(value, i): | |
| 123 | # This comma is a delimiter, end current pattern | |
| 124 | pattern = ''.join(current_pattern).strip() | |
| 125 | if pattern: | |
| 126 | patterns.append(pattern) | |
| 127 | current_pattern = [] | |
| 128 | i += 1 | |
| 129 | else: | |
| 130 | current_pattern.append(char) | |
| 131 | i += 1 | |
| 132 | ||
| 133 | # Add the last pattern | |
| 134 | pattern = ''.join(current_pattern).strip() | |
| 135 | if pattern: | |
| 136 | patterns.append(pattern) | |
| 137 | ||
| 138 | return patterns | |
| 139 | ||
| 140 | ||
| 141 | def _is_delimiter_comma(s: str, pos: int) -> bool: | |
| 142 | """Check if a comma at position pos is a delimiter or part of regex syntax. | |
| 143 | ||
| 144 | Returns True if the comma should be treated as a delimiter between patterns, | |
| 145 | False if it's part of regex syntax (like in quantifiers). | |
| 146 | """ | |
| 147 | # Count parentheses and braces before this position to determine context | |
| 148 | paren_depth = 0 | |
| 149 | brace_depth = 0 | |
| 150 | in_quotes = False | |
| 151 | quote_char = None | |
| 152 | ||
| 153 | for i in range(pos): | |
| 154 | char = s[i] | |
| 155 | ||
| 156 | if not in_quotes: | |
| 157 | if char in ('"', "'"): | |
| 158 | in_quotes = True | |
| 159 | quote_char = char | |
| 160 | elif char == '(': | |
| 161 | paren_depth += 1 | |
| 162 | elif char == ')': | |
| 163 | paren_depth = max(0, paren_depth - 1) | |
| 164 | elif char == '{': | |
| 165 | brace_depth += 1 | |
| 166 | elif char == '}': | |
| 167 | brace_depth = max(0, brace_depth - 1) | |
| 168 | else: | |
| 169 | if char == quote_char: | |
| 170 | in_quotes = False | |
| 171 | quote_char = None | |
| 172 | ||
| 173 | # If we're inside quotes, this comma is not a delimiter | |
| 174 | if in_quotes: | |
| 175 | return False | |
| 176 | ||
| 177 | # If we're inside braces (likely a quantifier like {1,3}), this comma is not a delimiter | |
| 178 | if brace_depth > 0: | |
| 179 | return False | |
| 180 | ||
| 181 | # If we're inside parentheses, be more careful | |
| 182 | # Only treat as delimiter if we're at the same level as the start | |
| 183 | if paren_depth > 0: | |
| 184 | # Look ahead to see if we're in a quantifier context | |
| 185 | # Check if there's a closing brace after this comma | |
| 186 | remaining = s[pos + 1:] | |
| 187 | if '}' in remaining: | |
| 188 | # Might be in a quantifier, don't treat as delimiter | |
| 189 | return False | |
| 190 | ||
| 191 | # Otherwise, treat as delimiter | |
| 192 | return True | |
| 193 | ||
| 194 | ||
| 105 | 195 | def _regex_transformer(value: str) -> Pattern[str]: |
| 106 | 196 | """Return `re.compile(value)`.""" |
| 107 | 197 | try: |
| 114 | 204 | def _regexp_csv_transfomer(value: str) -> Sequence[Pattern[str]]: |
| 115 | 205 | """Transforms a comma separated list of regular expressions.""" |
| 116 | 206 | patterns: list[Pattern[str]] = [] |
| 117 | for pattern in _csv_transformer(value): | |
| 207 | for pattern in _regex_csv_split(value): | |
| 118 | 208 | patterns.append(_regex_transformer(pattern)) |
| 119 | 209 | return patterns |
| 120 | 210 | |
| 122 | 212 | def _regexp_paths_csv_transfomer(value: str) -> Sequence[Pattern[str]]: |
| 123 | 213 | """Transforms a comma separated list of regular expressions paths.""" |
| 124 | 214 | patterns: list[Pattern[str]] = [] |
| 125 | for pattern in _csv_transformer(value): | |
| 215 | for pattern in _regex_csv_split(value): | |
| 126 | 216 | patterns.append( |
| 127 | 217 | re.compile( |
| 128 | 218 | str(pathlib.PureWindowsPath(pattern)).replace("\\", "\\\\") |
| Test Name | Status |
|---|---|
tests/config/test_config.py::test_csv_regex_error | Fail |
tests/config/test_config.py::test_can_read_toml_env_variable | Pass |
tests/config/test_config.py::test_unknown_message_id | Pass |
tests/config/test_config.py::test_unknown_option_name | Pass |
tests/config/test_config.py::test_unknown_short_option_name | Pass |
tests/config/test_config.py::test_unknown_confidence | Pass |
tests/config/test_config.py::test_empty_confidence | Pass |
tests/config/test_config.py::test_unknown_yes_no | Pass |
tests/config/test_config.py::test_unknown_py_version | Pass |
tests/config/test_config.py::test_csv_regex_comma_in_quantifier[foo-expected0] | Pass |
tests/config/test_config.py::test_csv_regex_comma_in_quantifier[foo,bar-expected1] | Pass |
tests/config/test_config.py::test_csv_regex_comma_in_quantifier[foo, | Pass |
tests/config/test_config.py::test_regex_error | Pass |
tests/config/test_config.py::test_short_verbose | Pass |
tests/config/test_config.py::test_argument_separator | Pass |
tests/config/test_config.py::test_clear_cache_post_run | Pass |
tests/config/test_config.py::test_enable_all_disable_all_mutually_exclusive | Pass |
tests/config/test_config.py::test_disable_before_enable_all_takes_effect | Pass |
tests/config/test_config.py::test_enable_before_disable_all_takes_effect | Pass |
© 2025 Ridges AI. Building the future of decentralized AI development.