Translate a pathname with shell wildcards to a regular expression. If `recursive` is true, the pattern segment '**' will match any number of path segments. If `include_hidden` is true, wildcards can match path segments beginning with a dot ('.'). If a sequence of separator cha
(pat, *, recursive=False, include_hidden=False, seps=None)
| 277 | |
| 278 | |
| 279 | def translate(pat, *, recursive=False, include_hidden=False, seps=None): |
| 280 | """Translate a pathname with shell wildcards to a regular expression. |
| 281 | |
| 282 | If `recursive` is true, the pattern segment '**' will match any number of |
| 283 | path segments. |
| 284 | |
| 285 | If `include_hidden` is true, wildcards can match path segments beginning |
| 286 | with a dot ('.'). |
| 287 | |
| 288 | If a sequence of separator characters is given to `seps`, they will be |
| 289 | used to split the pattern into segments and match path separators. If not |
| 290 | given, os.path.sep and os.path.altsep (where available) are used. |
| 291 | """ |
| 292 | if not seps: |
| 293 | if os.path.altsep: |
| 294 | seps = (os.path.sep, os.path.altsep) |
| 295 | else: |
| 296 | seps = os.path.sep |
| 297 | escaped_seps = ''.join(map(re.escape, seps)) |
| 298 | any_sep = f'[{escaped_seps}]' if len(seps) > 1 else escaped_seps |
| 299 | not_sep = f'[^{escaped_seps}]' |
| 300 | if include_hidden: |
| 301 | one_last_segment = f'{not_sep}+' |
| 302 | one_segment = f'{one_last_segment}{any_sep}' |
| 303 | any_segments = f'(?:.+{any_sep})?' |
| 304 | any_last_segments = '.*' |
| 305 | else: |
| 306 | one_last_segment = f'[^{escaped_seps}.]{not_sep}*' |
| 307 | one_segment = f'{one_last_segment}{any_sep}' |
| 308 | any_segments = f'(?:{one_segment})*' |
| 309 | any_last_segments = f'{any_segments}(?:{one_last_segment})?' |
| 310 | |
| 311 | results = [] |
| 312 | parts = re.split(any_sep, pat) |
| 313 | last_part_idx = len(parts) - 1 |
| 314 | for idx, part in enumerate(parts): |
| 315 | if part == '*': |
| 316 | results.append(one_segment if idx < last_part_idx else one_last_segment) |
| 317 | elif recursive and part == '**': |
| 318 | if idx < last_part_idx: |
| 319 | if parts[idx + 1] != '**': |
| 320 | results.append(any_segments) |
| 321 | else: |
| 322 | results.append(any_last_segments) |
| 323 | else: |
| 324 | if part: |
| 325 | if not include_hidden and part[0] in '*?': |
| 326 | results.append(r'(?!\.)') |
| 327 | results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep)[0]) |
| 328 | if idx < last_part_idx: |
| 329 | results.append(any_sep) |
| 330 | res = ''.join(results) |
| 331 | return fr'(?s:{res})\z' |
| 332 | |
| 333 | |
| 334 | @functools.lru_cache(maxsize=512) |