comparison env/lib/python3.7/site-packages/glob2/impl.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
comparison
equal deleted inserted replaced
4:79f47841a781 5:9b1c78e6ba9c
1 """Filename globbing utility."""
2
3 from __future__ import absolute_import
4
5 import sys
6 import os
7 import re
8 from os.path import join
9 from . import fnmatch
10
11 try:
12 from itertools import imap
13 except ImportError:
14 imap = map
15
16
17 class Globber(object):
18
19 listdir = staticmethod(os.listdir)
20 isdir = staticmethod(os.path.isdir)
21 islink = staticmethod(os.path.islink)
22 exists = staticmethod(os.path.lexists)
23
24 def walk(self, top, followlinks=False, sep=None):
25 """A simplified version of os.walk (code copied) that uses
26 ``self.listdir``, and the other local filesystem methods.
27
28 Because we don't care about file/directory distinctions, only
29 a single list is returned.
30 """
31 try:
32 names = self.listdir(top)
33 except os.error as err:
34 return
35
36 items = []
37 for name in names:
38 items.append(name)
39
40 yield top, items
41
42 for name in items:
43 new_path = _join_paths([top, name], sep=sep)
44 if followlinks or not self.islink(new_path):
45 for x in self.walk(new_path, followlinks):
46 yield x
47
48 def glob(self, pathname, with_matches=False, include_hidden=False, recursive=True,
49 norm_paths=True, case_sensitive=True, sep=None):
50 """Return a list of paths matching a pathname pattern.
51
52 The pattern may contain simple shell-style wildcards a la
53 fnmatch. However, unlike fnmatch, filenames starting with a
54 dot are special cases that are not matched by '*' and '?'
55 patterns.
56
57 If ``include_hidden`` is True, then files and folders starting with
58 a dot are also returned.
59 """
60 return list(self.iglob(pathname, with_matches, include_hidden,
61 norm_paths, case_sensitive, sep))
62
63 def iglob(self, pathname, with_matches=False, include_hidden=False, recursive=True,
64 norm_paths=True, case_sensitive=True, sep=None):
65 """Return an iterator which yields the paths matching a pathname
66 pattern.
67
68 The pattern may contain simple shell-style wildcards a la
69 fnmatch. However, unlike fnmatch, filenames starting with a
70 dot are special cases that are not matched by '*' and '?'
71 patterns.
72
73 If ``with_matches`` is True, then for each matching path
74 a 2-tuple will be returned; the second element if the tuple
75 will be a list of the parts of the path that matched the individual
76 wildcards.
77
78 If ``include_hidden`` is True, then files and folders starting with
79 a dot are also returned.
80 """
81 result = self._iglob(pathname, True, include_hidden,
82 norm_paths, case_sensitive, sep)
83 if with_matches:
84 return result
85 return imap(lambda s: s[0], result)
86
87 def _iglob(self, pathname, rootcall, include_hidden,
88 norm_paths, case_sensitive, sep):
89 """Internal implementation that backs :meth:`iglob`.
90
91 ``rootcall`` is required to differentiate between the user's call to
92 iglob(), and subsequent recursive calls, for the purposes of resolving
93 certain special cases of ** wildcards. Specifically, "**" is supposed
94 to include the current directory for purposes of globbing, but the
95 directory itself should never be returned. So if ** is the lastmost
96 part of the ``pathname`` given the user to the root call, we want to
97 ignore the current directory. For this, we need to know which the root
98 call is.
99 """
100
101 # Short-circuit if no glob magic
102 if not has_magic(pathname):
103 if self.exists(pathname):
104 yield pathname, ()
105 return
106
107 # If no directory part is left, assume the working directory
108 dirname, basename = os.path.split(pathname)
109
110 # If the directory is globbed, recurse to resolve.
111 # If at this point there is no directory part left, we simply
112 # continue with dirname="", which will search the current dir.
113 # `os.path.split()` returns the argument itself as a dirname if it is a
114 # drive or UNC path. Prevent an infinite recursion if a drive or UNC path
115 # contains magic characters (i.e. r'\\?\C:').
116 if dirname != pathname and has_magic(dirname):
117 # Note that this may return files, which will be ignored
118 # later when we try to use them as directories.
119 # Prefiltering them here would only require more IO ops.
120 dirs = self._iglob(dirname, False, include_hidden,
121 norm_paths, case_sensitive, sep)
122 else:
123 dirs = [(dirname, ())]
124
125 # Resolve ``basename`` expr for every directory found
126 for dirname, dir_groups in dirs:
127 for name, groups in self.resolve_pattern(dirname, basename,
128 not rootcall, include_hidden,
129 norm_paths, case_sensitive, sep):
130 yield _join_paths([dirname, name], sep=sep), dir_groups + groups
131
132 def resolve_pattern(self, dirname, pattern, globstar_with_root, include_hidden,
133 norm_paths, case_sensitive, sep):
134 """Apply ``pattern`` (contains no path elements) to the
135 literal directory in ``dirname``.
136
137 If pattern=='', this will filter for directories. This is
138 a special case that happens when the user's glob expression ends
139 with a slash (in which case we only want directories). It simpler
140 and faster to filter here than in :meth:`_iglob`.
141 """
142
143 if sys.version_info[0] == 3:
144 if isinstance(pattern, bytes):
145 dirname = bytes(os.curdir, 'ASCII')
146 else:
147 if isinstance(pattern, unicode) and not isinstance(dirname, unicode):
148 dirname = unicode(dirname, sys.getfilesystemencoding() or
149 sys.getdefaultencoding())
150
151 # If no magic, short-circuit, only check for existence
152 if not has_magic(pattern):
153 if pattern == '':
154 if self.isdir(dirname):
155 return [(pattern, ())]
156 else:
157 if self.exists(_join_paths([dirname, pattern], sep=sep)):
158 return [(pattern, ())]
159 return []
160
161 if not dirname:
162 dirname = os.curdir
163
164 try:
165 if pattern == '**':
166 # Include the current directory in **, if asked; by adding
167 # an empty string as opposed to '.', we spare ourselves
168 # having to deal with os.path.normpath() later.
169 names = [''] if globstar_with_root else []
170 for top, entries in self.walk(dirname, sep=sep):
171 _mkabs = lambda s: _join_paths([top[len(dirname) + 1:], s], sep=sep)
172 names.extend(map(_mkabs, entries))
173 # Reset pattern so that fnmatch(), which does not understand
174 # ** specifically, will only return a single group match.
175 pattern = '*'
176 else:
177 names = self.listdir(dirname)
178 except os.error:
179 return []
180
181 if not include_hidden and not _ishidden(pattern):
182 # Remove hidden files, but take care to ensure
183 # that the empty string we may have added earlier remains.
184 # Do not filter out the '' that we might have added earlier
185 names = filter(lambda x: not x or not _ishidden(x), names)
186 return fnmatch.filter(names, pattern, norm_paths, case_sensitive, sep)
187
188
189 default_globber = Globber()
190 glob = default_globber.glob
191 iglob = default_globber.iglob
192 del default_globber
193
194
195 magic_check = re.compile('[*?[]')
196 magic_check_bytes = re.compile(b'[*?[]')
197
198
199 def has_magic(s):
200 if isinstance(s, bytes):
201 match = magic_check_bytes.search(s)
202 else:
203 match = magic_check.search(s)
204 return match is not None
205
206
207 def _ishidden(path):
208 return path[0] in ('.', b'.'[0])
209
210
211 def _join_paths(paths, sep=None):
212 path = join(*paths)
213 if sep:
214 path = re.sub(r'\/', sep, path) # cached internally
215 return path
216