Returns a function that selects a given path and all its children, recursively, filtering by pattern.
(self, part, parts)
| 461 | return select_wildcard |
| 462 | |
| 463 | def recursive_selector(self, part, parts): |
| 464 | """Returns a function that selects a given path and all its children, |
| 465 | recursively, filtering by pattern. |
| 466 | """ |
| 467 | # Optimization: consume following '**' parts, which have no effect. |
| 468 | while parts and parts[-1] == '**': |
| 469 | parts.pop() |
| 470 | |
| 471 | # Optimization: consume and join any following non-special parts here, |
| 472 | # rather than leaving them for the next selector. They're used to |
| 473 | # build a regular expression, which we use to filter the results of |
| 474 | # the recursive walk. As a result, non-special pattern segments |
| 475 | # following a '**' wildcard don't require additional filesystem access |
| 476 | # to expand. |
| 477 | follow_symlinks = self.recursive is not _no_recurse_symlinks |
| 478 | if follow_symlinks: |
| 479 | while parts and parts[-1] not in _special_parts: |
| 480 | part += self.sep + parts.pop() |
| 481 | |
| 482 | match = None if part == '**' else self.compile(part) |
| 483 | dir_only = bool(parts) |
| 484 | select_next = self.selector(parts) |
| 485 | |
| 486 | def select_recursive(path, exists=False): |
| 487 | path_str = self.stringify_path(path) |
| 488 | match_pos = len(path_str) |
| 489 | if match is None or match(path_str, match_pos): |
| 490 | yield from select_next(path, exists) |
| 491 | stack = [path] |
| 492 | while stack: |
| 493 | yield from select_recursive_step(stack, match_pos) |
| 494 | |
| 495 | def select_recursive_step(stack, match_pos): |
| 496 | path = stack.pop() |
| 497 | try: |
| 498 | entries = self.scandir(path) |
| 499 | except OSError: |
| 500 | pass |
| 501 | else: |
| 502 | for entry, _entry_name, entry_path in entries: |
| 503 | is_dir = False |
| 504 | try: |
| 505 | if entry.is_dir(follow_symlinks=follow_symlinks): |
| 506 | is_dir = True |
| 507 | except OSError: |
| 508 | pass |
| 509 | |
| 510 | if is_dir or not dir_only: |
| 511 | entry_path_str = self.stringify_path(entry_path) |
| 512 | if dir_only: |
| 513 | entry_path = self.concat_path(entry_path, self.sep) |
| 514 | if match is None or match(entry_path_str, match_pos): |
| 515 | if dir_only: |
| 516 | yield from select_next(entry_path, exists=True) |
| 517 | else: |
| 518 | # Optimization: directly yield the path if this is |
| 519 | # last pattern part. |
| 520 | yield entry_path |