|
15 | 15 | from .exceptions import FSTimeoutError |
16 | 16 | from .implementations.local import LocalFileSystem, make_path_posix, trailing_sep |
17 | 17 | from .spec import AbstractBufferedFile, AbstractFileSystem |
18 | | -from .utils import is_exception, other_paths |
| 18 | +from .utils import glob_translate, is_exception, other_paths |
19 | 19 |
|
20 | 20 | private = re.compile("_[^_]") |
21 | 21 | iothread = [None] # dedicated fsspec IO thread |
@@ -745,8 +745,12 @@ async def _glob(self, path, maxdepth=None, **kwargs): |
745 | 745 |
|
746 | 746 | import re |
747 | 747 |
|
748 | | - ends = path.endswith("/") |
| 748 | + seps = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,) |
| 749 | + ends_with_sep = path.endswith(seps) # _strip_protocol strips trailing slash |
749 | 750 | path = self._strip_protocol(path) |
| 751 | + append_slash_to_dirname = ends_with_sep or path.endswith( |
| 752 | + tuple(sep + "**" for sep in seps) |
| 753 | + ) |
750 | 754 | idx_star = path.find("*") if path.find("*") >= 0 else len(path) |
751 | 755 | idx_qmark = path.find("?") if path.find("?") >= 0 else len(path) |
752 | 756 | idx_brace = path.find("[") if path.find("[") >= 0 else len(path) |
@@ -785,46 +789,22 @@ async def _glob(self, path, maxdepth=None, **kwargs): |
785 | 789 | allpaths = await self._find( |
786 | 790 | root, maxdepth=depth, withdirs=True, detail=True, **kwargs |
787 | 791 | ) |
788 | | - # Escape characters special to python regex, leaving our supported |
789 | | - # special characters in place. |
790 | | - # See https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html |
791 | | - # for shell globbing details. |
792 | | - pattern = ( |
793 | | - "^" |
794 | | - + ( |
795 | | - path.replace("\\", r"\\") |
796 | | - .replace(".", r"\.") |
797 | | - .replace("+", r"\+") |
798 | | - .replace("//", "/") |
799 | | - .replace("(", r"\(") |
800 | | - .replace(")", r"\)") |
801 | | - .replace("|", r"\|") |
802 | | - .replace("^", r"\^") |
803 | | - .replace("$", r"\$") |
804 | | - .replace("{", r"\{") |
805 | | - .replace("}", r"\}") |
806 | | - .rstrip("/") |
807 | | - .replace("?", ".") |
808 | | - ) |
809 | | - + "$" |
810 | | - ) |
811 | | - pattern = re.sub("/[*]{2}", "=SLASH_DOUBLE_STARS=", pattern) |
812 | | - pattern = re.sub("[*]{2}/?", "=DOUBLE_STARS=", pattern) |
813 | | - pattern = re.sub("[*]", "[^/]*", pattern) |
814 | | - pattern = re.sub("=SLASH_DOUBLE_STARS=", "(|/.*)", pattern) |
815 | | - pattern = re.sub("=DOUBLE_STARS=", ".*", pattern) |
| 792 | + |
| 793 | + pattern = glob_translate(path + ("/" if ends_with_sep else "")) |
816 | 794 | pattern = re.compile(pattern) |
| 795 | + |
817 | 796 | out = { |
818 | | - p: allpaths[p] |
819 | | - for p in sorted(allpaths) |
820 | | - if pattern.match(p.replace("//", "/").rstrip("/")) |
| 797 | + p: info |
| 798 | + for p, info in sorted(allpaths.items()) |
| 799 | + if pattern.match( |
| 800 | + ( |
| 801 | + p + "/" |
| 802 | + if append_slash_to_dirname and info["type"] == "directory" |
| 803 | + else p |
| 804 | + ) |
| 805 | + ) |
821 | 806 | } |
822 | 807 |
|
823 | | - # Return directories only when the glob end by a slash |
824 | | - # This is needed for posix glob compliance |
825 | | - if ends: |
826 | | - out = {k: v for k, v in out.items() if v["type"] == "directory"} |
827 | | - |
828 | 808 | if detail: |
829 | 809 | return out |
830 | 810 | else: |
|
0 commit comments