2
module with base functionality for std.path package
5
from __future__ import generators
9
def checktype(pathinstance, kw):
10
names = ('local', 'svnwc', 'svnurl', 'py', )
11
for name,value in kw.items():
13
cls = getattr(py.path, name)
14
if bool(isinstance(pathinstance, cls)) ^ bool(value):
20
""" deprecated: return checker callable checking for the given
21
kwargs-specified specification.
23
def __init__(self, **kwargs):
24
py.std.warnings.warn("py.path.checker is deprecated, construct "
25
"calls to pathobj.check() instead",
26
DeprecationWarning, stacklevel=2)
28
def __call__(self, p):
29
return p.check(**self.kwargs)
32
_depend_on_existence = 'exists', 'link'
34
def __init__(self, path):
38
raise NotImplementedError
40
def basename(self, arg):
41
return self.path.basename == arg
43
def basestarts(self, arg):
44
return self.path.basename.startswith(arg)
47
return self.path.relto(arg)
49
def fnmatch(self, arg):
50
return fnmatch(arg)(self.path)
52
def endswith(self, arg):
53
return str(self.path).endswith(arg)
55
def _evaluate(self, kw):
56
for name, value in kw.items():
60
meth = getattr(self, name)
61
except AttributeError:
65
meth = getattr(self, name[3:])
66
except AttributeError:
69
raise TypeError, "no %r checker available for %r" % (name, self.path)
71
if meth.im_func.func_code.co_argcount > 1:
72
if (not meth(value)) ^ invert:
75
if bool(value) ^ bool(meth()) ^ invert:
77
except (py.error.ENOENT, py.error.ENOTDIR):
78
for name in self._depend_on_existence:
91
class PathBase(object):
92
""" shared implementation for filesystem path objects."""
95
def check(self, **kw):
96
""" check a path for existence, or query its properties
98
without arguments, this returns True if the path exists (on the
99
filesystem), False if not
101
with (keyword only) arguments, the object compares the value
102
of the argument with the value of a property with the same name
103
(if it has one, else it raises a TypeError)
105
when for example the keyword argument 'ext' is '.py', this will
106
return True if self.ext == '.py', False otherwise
110
if not checktype(self, kw):
114
return self.Checkers(self)._evaluate(kw)
117
for i in self.listdir():
120
def __contains__(self, other):
121
if isinstance(other, str):
122
return self.join(other).check()
124
if other.dirpath() != self:
126
p = self.join(other.basename)
130
return self._getbyspec('basename')[0]
131
basename = property(basename, None, None, 'basename part of path')
133
def relto(self, relpath):
134
""" return a string which is the relative part of the path
135
to the given 'relpath'.
137
if not isinstance(relpath, (str, PathBase)):
138
raise TypeError("%r: not a string or path object" %(relpath,))
139
strrelpath = str(relpath)
140
if strrelpath and strrelpath[-1] != self.sep:
141
strrelpath += self.sep
142
#assert strrelpath[-1] == self.sep
143
#assert strrelpath[-2] != self.sep
145
if strself.startswith(strrelpath):
146
return strself[len(strrelpath):]
149
def parts(self, reverse=False):
150
""" return a root-first list of all ancestor directories
151
plus the path itself.
157
current = current.dirpath()
165
def common(self, other):
166
""" return the common part shared with the other path
167
or None if there is no common part.
170
for x, y in zip(self.parts(), other.parts()):
176
def __add__(self, other):
177
""" return new path object with 'other' added to the basename"""
178
return self.new(basename=self.basename+str(other))
180
def __cmp__(self, other):
181
""" return sort value (-1, 0, +1). """
183
return cmp(self.strpath, other.strpath)
184
except AttributeError:
185
return cmp(str(self), str(other)) # self.path, other.path)
188
""" return a string representation of this path. """
189
return repr(str(self))
191
def visit(self, fil=None, rec=None, ignore=_dummyclass):
192
""" yields all paths below the current one
194
fil is a filter (glob pattern or callable), if not matching the
195
path will not be yielded, defaulting to None (everything is
198
rec is a filter (glob pattern or callable) that controls whether
199
a node is descended, defaulting to None
201
ignore is an Exception class that is ignoredwhen calling dirlist()
202
on any of the paths (by default, all exceptions are reported)
204
if isinstance(fil, str):
207
if isinstance(rec, str):
209
elif not callable(rec):
213
current = reclist.pop(0)
215
dirlist = current.listdir()
219
if fil is None or fil(p):
221
if p.check(dir=1) and (rec is None or rec(p)):
224
def _callex(self, func, *args):
225
""" call a function and raise errno-exception if applicable. """
226
__tracebackhide__ = True
229
except py.error.Error:
231
except EnvironmentError, e:
232
if not hasattr(e, 'errno'):
234
__tracebackhide__ = False
235
cls, value, tb = sys.exc_info()
238
if not isinstance(e, WindowsError):
241
# we are not on Windows, or we got a proper OSError
242
cls = py.error._geterrnoclass(errno)
245
cls = py.error._getwinerrnoclass(errno)
248
value = cls("%s%r" % (func.__name__, args))
249
__tracebackhide__ = True
252
def _gethashinstance(self, hashtype):
253
if hashtype == "md5":
254
return py.std.md5.md5()
255
elif hashtype == "sha":
256
return py.std.sha.sha()
258
raise ValueError("unknown hash type: %r" %(hashtype,))
262
def __init__(self, pattern):
263
self.pattern = pattern
264
def __call__(self, path):
265
"""return true if the basename/fullname matches the glob-'pattern'.
268
? matches any single character
269
[seq] matches any character in seq
270
[!seq] matches any char not in seq
272
if the pattern contains a path-separator then the full path
273
is used for pattern matching and a '*' is prepended to the
276
if the pattern doesn't contain a path-separator the pattern
277
is only matched against the basename.
279
pattern = self.pattern
280
if pattern.find(path.sep) == -1:
283
name = str(path) # path.strpath # XXX svn?
284
pattern = '*' + path.sep + pattern
285
from fnmatch import fnmatch
286
return fnmatch(name, pattern)
289
class FSCheckers(Checkers):
290
_depend_on_existence = Checkers._depend_on_existence+('dir', 'file')
293
raise NotImplementedError
296
raise NotImplementedError
299
return self.path.basename.startswith('.')
302
if not arg.startswith('.'):
304
return self.path.ext == arg
306
class FSPathBase(PathBase):
307
""" shared implementation for filesystem path objects."""
308
Checkers = FSCheckers
310
def __div__(self, other):
311
return self.join(str(other))
313
def dirpath(self, *args, **kwargs):
314
""" return the directory Path of the current Path joined
315
with any given path arguments.
317
return self.new(basename='').join(*args, **kwargs)
320
""" extension of the path (including the '.')."""
321
return self._getbyspec('ext')[0]
322
ext = property(ext, None, None, 'extension part of path')
324
def purebasename(self):
325
""" pure base name of the path."""
326
return self._getbyspec('purebasename')[0]
327
purebasename = property(purebasename, None, None, 'basename without extension')
329
def read(self, mode='rb'):
330
""" read and return a bytestring from reading the path. """
331
if py.std.sys.version_info < (2,3):
334
mode = mode.replace(x, '')
341
def readlines(self, cr=1):
342
""" read and return a list of lines from the path. if cr is False, the
343
newline will be removed from the end of each line. """
345
content = self.read('rU')
346
return content.split('\n')
355
""" return object unpickled from self.read() """
358
from cPickle import load
359
return self._callex(load, f)
363
def move(self, target):
364
""" move this path to target. """
365
if target.relto(self):
366
raise py.error.EINVAL(target, "cannot move path into a subdirectory of itself")
369
except py.error.EXDEV: # invalid cross-device link
373
def _getpymodule(self):
374
"""resolve this path to a module python object. """
376
modname = modname.replace('.', self.sep)
378
return sys.modules[modname]
380
co = self._getpycodeobj()
381
mod = py.std.new.module(modname)
382
mod.__file__ = PathStr(self)
383
if self.basename == '__init__.py':
384
mod.__path__ = [str(self.dirpath())]
385
sys.modules[modname] = mod
387
exec co in mod.__dict__
389
del sys.modules[modname]
393
def _getpycodeobj(self):
394
""" read the path and compile it to a py.code.Code object. """
396
# XXX str(self) should show up somewhere in the code's filename
397
return py.code.compile(s)
400
def __init__(self, path):
401
global old_import_hook
403
if old_import_hook is None:
405
old_import_hook = __builtin__.__import__
406
__builtin__.__import__ = custom_import_hook
408
def relativeimport(p, name, parent=None):
409
names = name.split('.')
410
last_list = [False] * (len(names)-1) + [True]
412
for name, is_last in zip(names, last_list):
413
if hasattr(parent, name):
414
# shortcut if there is already the correct name
415
# in the parent package
416
submodule = getattr(parent, name)
418
if is_last and p.new(basename=name+'.py').check():
419
p = p.new(basename=name+'.py')
421
p = p.new(basename=name).join('__init__.py')
423
return None # not found
424
submodule = p._getpymodule()
425
if parent is not None:
426
setattr(parent, name, submodule)
427
modules.append(submodule)
429
return modules # success
432
old_import_hook = None
434
def custom_import_hook(name, glob=None, loc=None, fromlist=None):
435
__tracebackhide__ = False
436
__file__ = glob and glob.get('__file__')
437
if isinstance(__file__, PathStr):
438
# try to perform a relative import
439
# for cooperation with py.magic.autopath, first look in the pkgdir
441
if hasattr(__file__.__path__, 'pkgdir'):
442
modules = relativeimport(__file__.__path__.pkgdir, name)
444
modules = relativeimport(__file__.__path__, name)
447
submodule = modules[-1] # innermost submodule
448
# try to import submodules named in the 'fromlist' if the
449
# 'submodule' is a package
450
p = submodule.__file__.__path__
451
if p.check(basename='__init__.py'):
452
for name in fromlist:
453
relativeimport(p, name, parent=submodule)
457
return modules[0] # outermost package
459
__tracebackhide__ = True
460
return old_import_hook(name, glob, loc, fromlist)