PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/py/_path/common.py

https://bitbucket.org/dac_io/pypy
Python | 375 lines | 342 code | 20 blank | 13 comment | 20 complexity | f3ca82382f39b4569cc987dbc85ab485 MD5 | raw file
  1. """
  2. """
  3. import os, sys
  4. import py
  5. class Checkers:
  6. _depend_on_existence = 'exists', 'link', 'dir', 'file'
  7. def __init__(self, path):
  8. self.path = path
  9. def dir(self):
  10. raise NotImplementedError
  11. def file(self):
  12. raise NotImplementedError
  13. def dotfile(self):
  14. return self.path.basename.startswith('.')
  15. def ext(self, arg):
  16. if not arg.startswith('.'):
  17. arg = '.' + arg
  18. return self.path.ext == arg
  19. def exists(self):
  20. raise NotImplementedError
  21. def basename(self, arg):
  22. return self.path.basename == arg
  23. def basestarts(self, arg):
  24. return self.path.basename.startswith(arg)
  25. def relto(self, arg):
  26. return self.path.relto(arg)
  27. def fnmatch(self, arg):
  28. return self.path.fnmatch(arg)
  29. def endswith(self, arg):
  30. return str(self.path).endswith(arg)
  31. def _evaluate(self, kw):
  32. for name, value in kw.items():
  33. invert = False
  34. meth = None
  35. try:
  36. meth = getattr(self, name)
  37. except AttributeError:
  38. if name[:3] == 'not':
  39. invert = True
  40. try:
  41. meth = getattr(self, name[3:])
  42. except AttributeError:
  43. pass
  44. if meth is None:
  45. raise TypeError(
  46. "no %r checker available for %r" % (name, self.path))
  47. try:
  48. if py.code.getrawcode(meth).co_argcount > 1:
  49. if (not meth(value)) ^ invert:
  50. return False
  51. else:
  52. if bool(value) ^ bool(meth()) ^ invert:
  53. return False
  54. except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY):
  55. # EBUSY feels not entirely correct,
  56. # but its kind of necessary since ENOMEDIUM
  57. # is not accessible in python
  58. for name in self._depend_on_existence:
  59. if name in kw:
  60. if kw.get(name):
  61. return False
  62. name = 'not' + name
  63. if name in kw:
  64. if not kw.get(name):
  65. return False
  66. return True
  67. class NeverRaised(Exception):
  68. pass
  69. class PathBase(object):
  70. """ shared implementation for filesystem path objects."""
  71. Checkers = Checkers
  72. def __div__(self, other):
  73. return self.join(str(other))
  74. __truediv__ = __div__ # py3k
  75. def basename(self):
  76. """ basename part of path. """
  77. return self._getbyspec('basename')[0]
  78. basename = property(basename, None, None, basename.__doc__)
  79. def dirname(self):
  80. """ dirname part of path. """
  81. return self._getbyspec('dirname')[0]
  82. dirname = property(dirname, None, None, dirname.__doc__)
  83. def purebasename(self):
  84. """ pure base name of the path."""
  85. return self._getbyspec('purebasename')[0]
  86. purebasename = property(purebasename, None, None, purebasename.__doc__)
  87. def ext(self):
  88. """ extension of the path (including the '.')."""
  89. return self._getbyspec('ext')[0]
  90. ext = property(ext, None, None, ext.__doc__)
  91. def dirpath(self, *args, **kwargs):
  92. """ return the directory Path of the current Path joined
  93. with any given path arguments.
  94. """
  95. return self.new(basename='').join(*args, **kwargs)
  96. def read(self, mode='r'):
  97. """ read and return a bytestring from reading the path. """
  98. if sys.version_info < (2,3):
  99. for x in 'u', 'U':
  100. if x in mode:
  101. mode = mode.replace(x, '')
  102. f = self.open(mode)
  103. try:
  104. return f.read()
  105. finally:
  106. f.close()
  107. def readlines(self, cr=1):
  108. """ read and return a list of lines from the path. if cr is False, the
  109. newline will be removed from the end of each line. """
  110. if not cr:
  111. content = self.read('rU')
  112. return content.split('\n')
  113. else:
  114. f = self.open('rU')
  115. try:
  116. return f.readlines()
  117. finally:
  118. f.close()
  119. def load(self):
  120. """ (deprecated) return object unpickled from self.read() """
  121. f = self.open('rb')
  122. try:
  123. return py.error.checked_call(py.std.pickle.load, f)
  124. finally:
  125. f.close()
  126. def move(self, target):
  127. """ move this path to target. """
  128. if target.relto(self):
  129. raise py.error.EINVAL(target,
  130. "cannot move path into a subdirectory of itself")
  131. try:
  132. self.rename(target)
  133. except py.error.EXDEV: # invalid cross-device link
  134. self.copy(target)
  135. self.remove()
  136. def __repr__(self):
  137. """ return a string representation of this path. """
  138. return repr(str(self))
  139. def check(self, **kw):
  140. """ check a path for existence and properties.
  141. Without arguments, return True if the path exists, otherwise False.
  142. valid checkers::
  143. file=1 # is a file
  144. file=0 # is not a file (may not even exist)
  145. dir=1 # is a dir
  146. link=1 # is a link
  147. exists=1 # exists
  148. You can specify multiple checker definitions, for example::
  149. path.check(file=1, link=1) # a link pointing to a file
  150. """
  151. if not kw:
  152. kw = {'exists' : 1}
  153. return self.Checkers(self)._evaluate(kw)
  154. def fnmatch(self, pattern):
  155. """return true if the basename/fullname matches the glob-'pattern'.
  156. valid pattern characters::
  157. * matches everything
  158. ? matches any single character
  159. [seq] matches any character in seq
  160. [!seq] matches any char not in seq
  161. If the pattern contains a path-separator then the full path
  162. is used for pattern matching and a '*' is prepended to the
  163. pattern.
  164. if the pattern doesn't contain a path-separator the pattern
  165. is only matched against the basename.
  166. """
  167. return FNMatcher(pattern)(self)
  168. def relto(self, relpath):
  169. """ return a string which is the relative part of the path
  170. to the given 'relpath'.
  171. """
  172. if not isinstance(relpath, (str, PathBase)):
  173. raise TypeError("%r: not a string or path object" %(relpath,))
  174. strrelpath = str(relpath)
  175. if strrelpath and strrelpath[-1] != self.sep:
  176. strrelpath += self.sep
  177. #assert strrelpath[-1] == self.sep
  178. #assert strrelpath[-2] != self.sep
  179. strself = str(self)
  180. if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
  181. if os.path.normcase(strself).startswith(
  182. os.path.normcase(strrelpath)):
  183. return strself[len(strrelpath):]
  184. elif strself.startswith(strrelpath):
  185. return strself[len(strrelpath):]
  186. return ""
  187. def bestrelpath(self, dest):
  188. """ return a string which is a relative path from self
  189. (assumed to be a directory) to dest such that
  190. self.join(bestrelpath) == dest and if not such
  191. path can be determined return dest.
  192. """
  193. try:
  194. if self == dest:
  195. return os.curdir
  196. base = self.common(dest)
  197. if not base: # can be the case on windows
  198. return str(dest)
  199. self2base = self.relto(base)
  200. reldest = dest.relto(base)
  201. if self2base:
  202. n = self2base.count(self.sep) + 1
  203. else:
  204. n = 0
  205. l = [os.pardir] * n
  206. if reldest:
  207. l.append(reldest)
  208. target = dest.sep.join(l)
  209. return target
  210. except AttributeError:
  211. return str(dest)
  212. def parts(self, reverse=False):
  213. """ return a root-first list of all ancestor directories
  214. plus the path itself.
  215. """
  216. current = self
  217. l = [self]
  218. while 1:
  219. last = current
  220. current = current.dirpath()
  221. if last == current:
  222. break
  223. l.insert(0, current)
  224. if reverse:
  225. l.reverse()
  226. return l
  227. def common(self, other):
  228. """ return the common part shared with the other path
  229. or None if there is no common part.
  230. """
  231. last = None
  232. for x, y in zip(self.parts(), other.parts()):
  233. if x != y:
  234. return last
  235. last = x
  236. return last
  237. def __add__(self, other):
  238. """ return new path object with 'other' added to the basename"""
  239. return self.new(basename=self.basename+str(other))
  240. def __cmp__(self, other):
  241. """ return sort value (-1, 0, +1). """
  242. try:
  243. return cmp(self.strpath, other.strpath)
  244. except AttributeError:
  245. return cmp(str(self), str(other)) # self.path, other.path)
  246. def __lt__(self, other):
  247. try:
  248. return self.strpath < other.strpath
  249. except AttributeError:
  250. return str(self) < str(other)
  251. def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
  252. """ yields all paths below the current one
  253. fil is a filter (glob pattern or callable), if not matching the
  254. path will not be yielded, defaulting to None (everything is
  255. returned)
  256. rec is a filter (glob pattern or callable) that controls whether
  257. a node is descended, defaulting to None
  258. ignore is an Exception class that is ignoredwhen calling dirlist()
  259. on any of the paths (by default, all exceptions are reported)
  260. bf if True will cause a breadthfirst search instead of the
  261. default depthfirst. Default: False
  262. sort if True will sort entries within each directory level.
  263. """
  264. for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
  265. yield x
  266. def _sortlist(self, res, sort):
  267. if sort:
  268. if hasattr(sort, '__call__'):
  269. res.sort(sort)
  270. else:
  271. res.sort()
  272. def samefile(self, other):
  273. """ return True if other refers to the same stat object as self. """
  274. return self.strpath == str(other)
  275. class Visitor:
  276. def __init__(self, fil, rec, ignore, bf, sort):
  277. if isinstance(fil, str):
  278. fil = FNMatcher(fil)
  279. if isinstance(rec, str):
  280. self.rec = fnmatch(fil)
  281. elif not hasattr(rec, '__call__') and rec:
  282. self.rec = lambda path: True
  283. else:
  284. self.rec = rec
  285. self.fil = fil
  286. self.ignore = ignore
  287. self.breadthfirst = bf
  288. self.optsort = sort and sorted or (lambda x: x)
  289. def gen(self, path):
  290. try:
  291. entries = path.listdir()
  292. except self.ignore:
  293. return
  294. rec = self.rec
  295. dirs = self.optsort([p for p in entries
  296. if p.check(dir=1) and (rec is None or rec(p))])
  297. if not self.breadthfirst:
  298. for subdir in dirs:
  299. for p in self.gen(subdir):
  300. yield p
  301. for p in self.optsort(entries):
  302. if self.fil is None or self.fil(p):
  303. yield p
  304. if self.breadthfirst:
  305. for subdir in dirs:
  306. for p in self.gen(subdir):
  307. yield p
  308. class FNMatcher:
  309. def __init__(self, pattern):
  310. self.pattern = pattern
  311. def __call__(self, path):
  312. pattern = self.pattern
  313. if pattern.find(path.sep) == -1:
  314. name = path.basename
  315. else:
  316. name = str(path) # path.strpath # XXX svn?
  317. pattern = '*' + path.sep + pattern
  318. return py.std.fnmatch.fnmatch(name, pattern)