Coverage for /usr/local/lib/python3.7/site-packages/py/_path/common.py : 32%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2"""
3import warnings
4import os
5import sys
6import posixpath
7import fnmatch
8import py
10# Moved from local.py.
11iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt')
13try:
14 from os import fspath
15except ImportError:
16 def fspath(path):
17 """
18 Return the string representation of the path.
19 If str or bytes is passed in, it is returned unchanged.
20 This code comes from PEP 519, modified to support earlier versions of
21 python.
23 This is required for python < 3.6.
24 """
25 if isinstance(path, (py.builtin.text, py.builtin.bytes)):
26 return path
28 # Work from the object's type to match method resolution of other magic
29 # methods.
30 path_type = type(path)
31 try:
32 return path_type.__fspath__(path)
33 except AttributeError:
34 if hasattr(path_type, '__fspath__'):
35 raise
36 try:
37 import pathlib
38 except ImportError:
39 pass
40 else:
41 if isinstance(path, pathlib.PurePath):
42 return py.builtin.text(path)
44 raise TypeError("expected str, bytes or os.PathLike object, not "
45 + path_type.__name__)
47class Checkers:
48 _depend_on_existence = 'exists', 'link', 'dir', 'file'
50 def __init__(self, path):
51 self.path = path
53 def dir(self):
54 raise NotImplementedError
56 def file(self):
57 raise NotImplementedError
59 def dotfile(self):
60 return self.path.basename.startswith('.')
62 def ext(self, arg):
63 if not arg.startswith('.'):
64 arg = '.' + arg
65 return self.path.ext == arg
67 def exists(self):
68 raise NotImplementedError
70 def basename(self, arg):
71 return self.path.basename == arg
73 def basestarts(self, arg):
74 return self.path.basename.startswith(arg)
76 def relto(self, arg):
77 return self.path.relto(arg)
79 def fnmatch(self, arg):
80 return self.path.fnmatch(arg)
82 def endswith(self, arg):
83 return str(self.path).endswith(arg)
85 def _evaluate(self, kw):
86 for name, value in kw.items():
87 invert = False
88 meth = None
89 try:
90 meth = getattr(self, name)
91 except AttributeError:
92 if name[:3] == 'not':
93 invert = True
94 try:
95 meth = getattr(self, name[3:])
96 except AttributeError:
97 pass
98 if meth is None:
99 raise TypeError(
100 "no %r checker available for %r" % (name, self.path))
101 try:
102 if py.code.getrawcode(meth).co_argcount > 1:
103 if (not meth(value)) ^ invert:
104 return False
105 else:
106 if bool(value) ^ bool(meth()) ^ invert:
107 return False
108 except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY):
109 # EBUSY feels not entirely correct,
110 # but its kind of necessary since ENOMEDIUM
111 # is not accessible in python
112 for name in self._depend_on_existence:
113 if name in kw:
114 if kw.get(name):
115 return False
116 name = 'not' + name
117 if name in kw:
118 if not kw.get(name):
119 return False
120 return True
122class NeverRaised(Exception):
123 pass
125class PathBase(object):
126 """ shared implementation for filesystem path objects."""
127 Checkers = Checkers
129 def __div__(self, other):
130 return self.join(fspath(other))
131 __truediv__ = __div__ # py3k
133 def basename(self):
134 """ basename part of path. """
135 return self._getbyspec('basename')[0]
136 basename = property(basename, None, None, basename.__doc__)
138 def dirname(self):
139 """ dirname part of path. """
140 return self._getbyspec('dirname')[0]
141 dirname = property(dirname, None, None, dirname.__doc__)
143 def purebasename(self):
144 """ pure base name of the path."""
145 return self._getbyspec('purebasename')[0]
146 purebasename = property(purebasename, None, None, purebasename.__doc__)
148 def ext(self):
149 """ extension of the path (including the '.')."""
150 return self._getbyspec('ext')[0]
151 ext = property(ext, None, None, ext.__doc__)
153 def dirpath(self, *args, **kwargs):
154 """ return the directory path joined with any given path arguments. """
155 return self.new(basename='').join(*args, **kwargs)
157 def read_binary(self):
158 """ read and return a bytestring from reading the path. """
159 with self.open('rb') as f:
160 return f.read()
162 def read_text(self, encoding):
163 """ read and return a Unicode string from reading the path. """
164 with self.open("r", encoding=encoding) as f:
165 return f.read()
168 def read(self, mode='r'):
169 """ read and return a bytestring from reading the path. """
170 with self.open(mode) as f:
171 return f.read()
173 def readlines(self, cr=1):
174 """ read and return a list of lines from the path. if cr is False, the
175newline will be removed from the end of each line. """
176 if sys.version_info < (3, ):
177 mode = 'rU'
178 else: # python 3 deprecates mode "U" in favor of "newline" option
179 mode = 'r'
181 if not cr:
182 content = self.read(mode)
183 return content.split('\n')
184 else:
185 f = self.open(mode)
186 try:
187 return f.readlines()
188 finally:
189 f.close()
191 def load(self):
192 """ (deprecated) return object unpickled from self.read() """
193 f = self.open('rb')
194 try:
195 import pickle
196 return py.error.checked_call(pickle.load, f)
197 finally:
198 f.close()
200 def move(self, target):
201 """ move this path to target. """
202 if target.relto(self):
203 raise py.error.EINVAL(
204 target,
205 "cannot move path into a subdirectory of itself")
206 try:
207 self.rename(target)
208 except py.error.EXDEV: # invalid cross-device link
209 self.copy(target)
210 self.remove()
212 def __repr__(self):
213 """ return a string representation of this path. """
214 return repr(str(self))
216 def check(self, **kw):
217 """ check a path for existence and properties.
219 Without arguments, return True if the path exists, otherwise False.
221 valid checkers::
223 file=1 # is a file
224 file=0 # is not a file (may not even exist)
225 dir=1 # is a dir
226 link=1 # is a link
227 exists=1 # exists
229 You can specify multiple checker definitions, for example::
231 path.check(file=1, link=1) # a link pointing to a file
232 """
233 if not kw:
234 kw = {'exists': 1}
235 return self.Checkers(self)._evaluate(kw)
237 def fnmatch(self, pattern):
238 """return true if the basename/fullname matches the glob-'pattern'.
240 valid pattern characters::
242 * matches everything
243 ? matches any single character
244 [seq] matches any character in seq
245 [!seq] matches any char not in seq
247 If the pattern contains a path-separator then the full path
248 is used for pattern matching and a '*' is prepended to the
249 pattern.
251 if the pattern doesn't contain a path-separator the pattern
252 is only matched against the basename.
253 """
254 return FNMatcher(pattern)(self)
256 def relto(self, relpath):
257 """ return a string which is the relative part of the path
258 to the given 'relpath'.
259 """
260 if not isinstance(relpath, (str, PathBase)):
261 raise TypeError("%r: not a string or path object" %(relpath,))
262 strrelpath = str(relpath)
263 if strrelpath and strrelpath[-1] != self.sep:
264 strrelpath += self.sep
265 #assert strrelpath[-1] == self.sep
266 #assert strrelpath[-2] != self.sep
267 strself = self.strpath
268 if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
269 if os.path.normcase(strself).startswith(
270 os.path.normcase(strrelpath)):
271 return strself[len(strrelpath):]
272 elif strself.startswith(strrelpath):
273 return strself[len(strrelpath):]
274 return ""
276 def ensure_dir(self, *args):
277 """ ensure the path joined with args is a directory. """
278 return self.ensure(*args, **{"dir": True})
280 def bestrelpath(self, dest):
281 """ return a string which is a relative path from self
282 (assumed to be a directory) to dest such that
283 self.join(bestrelpath) == dest and if not such
284 path can be determined return dest.
285 """
286 try:
287 if self == dest:
288 return os.curdir
289 base = self.common(dest)
290 if not base: # can be the case on windows
291 return str(dest)
292 self2base = self.relto(base)
293 reldest = dest.relto(base)
294 if self2base:
295 n = self2base.count(self.sep) + 1
296 else:
297 n = 0
298 l = [os.pardir] * n
299 if reldest:
300 l.append(reldest)
301 target = dest.sep.join(l)
302 return target
303 except AttributeError:
304 return str(dest)
306 def exists(self):
307 return self.check()
309 def isdir(self):
310 return self.check(dir=1)
312 def isfile(self):
313 return self.check(file=1)
315 def parts(self, reverse=False):
316 """ return a root-first list of all ancestor directories
317 plus the path itself.
318 """
319 current = self
320 l = [self]
321 while 1:
322 last = current
323 current = current.dirpath()
324 if last == current:
325 break
326 l.append(current)
327 if not reverse:
328 l.reverse()
329 return l
331 def common(self, other):
332 """ return the common part shared with the other path
333 or None if there is no common part.
334 """
335 last = None
336 for x, y in zip(self.parts(), other.parts()):
337 if x != y:
338 return last
339 last = x
340 return last
342 def __add__(self, other):
343 """ return new path object with 'other' added to the basename"""
344 return self.new(basename=self.basename+str(other))
346 def __cmp__(self, other):
347 """ return sort value (-1, 0, +1). """
348 try:
349 return cmp(self.strpath, other.strpath)
350 except AttributeError:
351 return cmp(str(self), str(other)) # self.path, other.path)
353 def __lt__(self, other):
354 try:
355 return self.strpath < other.strpath
356 except AttributeError:
357 return str(self) < str(other)
359 def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
360 """ yields all paths below the current one
362 fil is a filter (glob pattern or callable), if not matching the
363 path will not be yielded, defaulting to None (everything is
364 returned)
366 rec is a filter (glob pattern or callable) that controls whether
367 a node is descended, defaulting to None
369 ignore is an Exception class that is ignoredwhen calling dirlist()
370 on any of the paths (by default, all exceptions are reported)
372 bf if True will cause a breadthfirst search instead of the
373 default depthfirst. Default: False
375 sort if True will sort entries within each directory level.
376 """
377 for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
378 yield x
380 def _sortlist(self, res, sort):
381 if sort:
382 if hasattr(sort, '__call__'):
383 warnings.warn(DeprecationWarning(
384 "listdir(sort=callable) is deprecated and breaks on python3"
385 ), stacklevel=3)
386 res.sort(sort)
387 else:
388 res.sort()
390 def samefile(self, other):
391 """ return True if other refers to the same stat object as self. """
392 return self.strpath == str(other)
394 def __fspath__(self):
395 return self.strpath
397class Visitor:
398 def __init__(self, fil, rec, ignore, bf, sort):
399 if isinstance(fil, py.builtin._basestring):
400 fil = FNMatcher(fil)
401 if isinstance(rec, py.builtin._basestring):
402 self.rec = FNMatcher(rec)
403 elif not hasattr(rec, '__call__') and rec:
404 self.rec = lambda path: True
405 else:
406 self.rec = rec
407 self.fil = fil
408 self.ignore = ignore
409 self.breadthfirst = bf
410 self.optsort = sort and sorted or (lambda x: x)
412 def gen(self, path):
413 try:
414 entries = path.listdir()
415 except self.ignore:
416 return
417 rec = self.rec
418 dirs = self.optsort([p for p in entries
419 if p.check(dir=1) and (rec is None or rec(p))])
420 if not self.breadthfirst:
421 for subdir in dirs:
422 for p in self.gen(subdir):
423 yield p
424 for p in self.optsort(entries):
425 if self.fil is None or self.fil(p):
426 yield p
427 if self.breadthfirst:
428 for subdir in dirs:
429 for p in self.gen(subdir):
430 yield p
432class FNMatcher:
433 def __init__(self, pattern):
434 self.pattern = pattern
436 def __call__(self, path):
437 pattern = self.pattern
439 if (pattern.find(path.sep) == -1 and
440 iswin32 and
441 pattern.find(posixpath.sep) != -1):
442 # Running on Windows, the pattern has no Windows path separators,
443 # and the pattern has one or more Posix path separators. Replace
444 # the Posix path separators with the Windows path separator.
445 pattern = pattern.replace(posixpath.sep, path.sep)
447 if pattern.find(path.sep) == -1:
448 name = path.basename
449 else:
450 name = str(path) # path.strpath # XXX svn?
451 if not os.path.isabs(pattern):
452 pattern = '*' + path.sep + pattern
453 return fnmatch.fnmatch(name, pattern)