~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to py/path/common.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
module with base functionality for std.path package
 
3
 
 
4
"""
 
5
from __future__ import generators
 
6
import os, sys
 
7
import py
 
8
 
 
9
def checktype(pathinstance, kw):
 
10
    names = ('local', 'svnwc', 'svnurl', 'py', )
 
11
    for name,value in kw.items():
 
12
        if name in names:
 
13
            cls = getattr(py.path, name)
 
14
            if bool(isinstance(pathinstance, cls)) ^ bool(value):
 
15
                return False
 
16
            del kw[name]
 
17
    return True
 
18
 
 
19
class checker:
 
20
    """ deprecated: return checker callable checking for the given 
 
21
        kwargs-specified specification. 
 
22
    """
 
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)
 
27
        self.kwargs = kwargs
 
28
    def __call__(self, p):
 
29
        return p.check(**self.kwargs)
 
30
 
 
31
class Checkers:
 
32
    _depend_on_existence = 'exists', 'link'
 
33
 
 
34
    def __init__(self, path):
 
35
        self.path = path
 
36
 
 
37
    def exists(self):
 
38
        raise NotImplementedError
 
39
 
 
40
    def basename(self, arg):
 
41
        return self.path.basename == arg
 
42
 
 
43
    def basestarts(self, arg):
 
44
        return self.path.basename.startswith(arg)
 
45
 
 
46
    def relto(self, arg):
 
47
        return self.path.relto(arg)
 
48
 
 
49
    def fnmatch(self, arg):
 
50
        return fnmatch(arg)(self.path)
 
51
 
 
52
    def endswith(self, arg):
 
53
        return str(self.path).endswith(arg)
 
54
 
 
55
    def _evaluate(self, kw):
 
56
        for name, value in kw.items():
 
57
            invert = False
 
58
            meth = None
 
59
            try:
 
60
                meth = getattr(self, name)
 
61
            except AttributeError:
 
62
                if name[:3] == 'not':
 
63
                    invert = True
 
64
                    try:
 
65
                        meth = getattr(self, name[3:])
 
66
                    except AttributeError:
 
67
                        pass
 
68
            if meth is None:
 
69
                raise TypeError, "no %r checker available for %r" % (name, self.path)
 
70
            try:
 
71
                if meth.im_func.func_code.co_argcount > 1:
 
72
                    if (not meth(value)) ^ invert:
 
73
                        return False
 
74
                else:
 
75
                    if bool(value) ^ bool(meth()) ^ invert:
 
76
                        return False
 
77
            except (py.error.ENOENT, py.error.ENOTDIR):
 
78
                for name in self._depend_on_existence:
 
79
                    if name in kw:
 
80
                        if kw.get(name):
 
81
                            return False
 
82
                    name = 'not' + name
 
83
                    if name in kw:
 
84
                        if not kw.get(name):
 
85
                            return False
 
86
        return True
 
87
 
 
88
class _dummyclass: 
 
89
    pass
 
90
 
 
91
class PathBase(object):
 
92
    """ shared implementation for filesystem path objects."""
 
93
    Checkers = Checkers
 
94
 
 
95
    def check(self, **kw):
 
96
        """ check a path for existence, or query its properties
 
97
 
 
98
            without arguments, this returns True if the path exists (on the
 
99
            filesystem), False if not
 
100
 
 
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)
 
104
 
 
105
            when for example the keyword argument 'ext' is '.py', this will
 
106
            return True if self.ext == '.py', False otherwise
 
107
        """
 
108
        if kw:
 
109
            kw = kw.copy()
 
110
            if not checktype(self, kw):
 
111
                return False
 
112
        else:
 
113
            kw = {'exists' : 1}
 
114
        return self.Checkers(self)._evaluate(kw)
 
115
 
 
116
    def __iter__(self):
 
117
        for i in self.listdir():
 
118
            yield i
 
119
 
 
120
    def __contains__(self, other):
 
121
        if isinstance(other, str):
 
122
            return self.join(other).check()
 
123
        else:
 
124
            if other.dirpath() != self:
 
125
                return False
 
126
            p = self.join(other.basename)
 
127
            return p.check()
 
128
 
 
129
    def basename(self):
 
130
        return self._getbyspec('basename')[0]
 
131
    basename = property(basename, None, None, 'basename part of path')
 
132
 
 
133
    def relto(self, relpath):
 
134
        """ return a string which is the relative part of the path
 
135
        to the given 'relpath'. 
 
136
        """
 
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
 
144
        strself = str(self)
 
145
        if strself.startswith(strrelpath):
 
146
            return strself[len(strrelpath):]
 
147
        return ""
 
148
 
 
149
    def parts(self, reverse=False):
 
150
        """ return a root-first list of all ancestor directories
 
151
            plus the path itself.
 
152
        """
 
153
        current = self
 
154
        l = [self]
 
155
        while 1:
 
156
            last = current
 
157
            current = current.dirpath()
 
158
            if last == current:
 
159
                break
 
160
            l.insert(0, current)
 
161
        if reverse:
 
162
            l.reverse()
 
163
        return l
 
164
 
 
165
    def common(self, other):
 
166
        """ return the common part shared with the other path
 
167
            or None if there is no common part.
 
168
        """
 
169
        last = None
 
170
        for x, y in zip(self.parts(), other.parts()):
 
171
            if x != y:
 
172
                return last
 
173
            last = x
 
174
        return last
 
175
 
 
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))
 
179
 
 
180
    def __cmp__(self, other):
 
181
        """ return sort value (-1, 0, +1). """
 
182
        try:
 
183
            return cmp(self.strpath, other.strpath)
 
184
        except AttributeError:
 
185
            return cmp(str(self), str(other)) # self.path, other.path)
 
186
 
 
187
    def __repr__(self):
 
188
        """ return a string representation of this path. """
 
189
        return repr(str(self))
 
190
 
 
191
    def visit(self, fil=None, rec=None, ignore=_dummyclass):
 
192
        """ yields all paths below the current one
 
193
 
 
194
            fil is a filter (glob pattern or callable), if not matching the
 
195
            path will not be yielded, defaulting to None (everything is
 
196
            returned)
 
197
 
 
198
            rec is a filter (glob pattern or callable) that controls whether
 
199
            a node is descended, defaulting to None
 
200
 
 
201
            ignore is an Exception class that is ignoredwhen calling dirlist()
 
202
            on any of the paths (by default, all exceptions are reported)
 
203
        """
 
204
        if isinstance(fil, str):
 
205
            fil = fnmatch(fil)
 
206
        if rec: 
 
207
            if isinstance(rec, str):
 
208
                rec = fnmatch(fil)
 
209
            elif not callable(rec): 
 
210
                rec = lambda x: True 
 
211
        reclist = [self]
 
212
        while reclist: 
 
213
            current = reclist.pop(0)
 
214
            try:
 
215
                dirlist = current.listdir() 
 
216
            except ignore:
 
217
                return
 
218
            for p in dirlist:
 
219
                if fil is None or fil(p):
 
220
                    yield p
 
221
                if p.check(dir=1) and (rec is None or rec(p)):
 
222
                    reclist.append(p)
 
223
 
 
224
    def _callex(self, func, *args):
 
225
        """ call a function and raise errno-exception if applicable. """
 
226
        __tracebackhide__ = True
 
227
        try:
 
228
            return func(*args)
 
229
        except py.error.Error: 
 
230
            raise
 
231
        except EnvironmentError, e:
 
232
            if not hasattr(e, 'errno'):
 
233
                raise
 
234
            __tracebackhide__ = False
 
235
            cls, value, tb = sys.exc_info()
 
236
            errno = e.errno 
 
237
            try:
 
238
                if not isinstance(e, WindowsError): 
 
239
                    raise NameError
 
240
            except NameError: 
 
241
                # we are not on Windows, or we got a proper OSError
 
242
                cls = py.error._geterrnoclass(errno)
 
243
            else: 
 
244
                try: 
 
245
                    cls = py.error._getwinerrnoclass(errno)
 
246
                except KeyError:    
 
247
                    raise cls, value, tb
 
248
            value = cls("%s%r" % (func.__name__, args))
 
249
            __tracebackhide__ = True
 
250
            raise cls, value
 
251
 
 
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()
 
257
        else:
 
258
            raise ValueError("unknown hash type: %r" %(hashtype,))
 
259
 
 
260
 
 
261
class fnmatch:
 
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'.
 
266
 
 
267
        *       matches everything
 
268
        ?       matches any single character
 
269
        [seq]   matches any character in seq
 
270
        [!seq]  matches any char not in seq
 
271
 
 
272
        if the pattern contains a path-separator then the full path
 
273
        is used for pattern matching and a '*' is prepended to the
 
274
        pattern.
 
275
 
 
276
        if the pattern doesn't contain a path-separator the pattern
 
277
        is only matched against the basename.
 
278
        """
 
279
        pattern = self.pattern
 
280
        if pattern.find(path.sep) == -1:
 
281
            name = path.basename
 
282
        else:
 
283
            name = str(path) # path.strpath # XXX svn?
 
284
            pattern = '*' + path.sep + pattern
 
285
        from fnmatch import fnmatch
 
286
        return fnmatch(name, pattern)
 
287
 
 
288
 
 
289
class FSCheckers(Checkers):
 
290
    _depend_on_existence = Checkers._depend_on_existence+('dir', 'file')
 
291
 
 
292
    def dir(self):
 
293
        raise NotImplementedError
 
294
 
 
295
    def file(self):
 
296
        raise NotImplementedError
 
297
 
 
298
    def dotfile(self):
 
299
        return self.path.basename.startswith('.')
 
300
 
 
301
    def ext(self, arg):
 
302
        if not arg.startswith('.'):
 
303
            arg = '.' + arg
 
304
        return self.path.ext == arg
 
305
 
 
306
class FSPathBase(PathBase):
 
307
    """ shared implementation for filesystem path objects."""
 
308
    Checkers = FSCheckers
 
309
 
 
310
    def __div__(self, other):
 
311
        return self.join(str(other))
 
312
 
 
313
    def dirpath(self, *args, **kwargs):
 
314
        """ return the directory Path of the current Path joined
 
315
            with any given path arguments.
 
316
        """
 
317
        return self.new(basename='').join(*args, **kwargs)
 
318
 
 
319
    def ext(self):
 
320
        """ extension of the path (including the '.')."""
 
321
        return self._getbyspec('ext')[0]
 
322
    ext = property(ext, None, None, 'extension part of path')
 
323
 
 
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')
 
328
 
 
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):
 
332
            for x in 'u', 'U':
 
333
                if x in mode:
 
334
                    mode = mode.replace(x, '')
 
335
        f = self.open(mode)
 
336
        try:
 
337
            return f.read()
 
338
        finally:
 
339
            f.close()
 
340
 
 
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. """
 
344
        if not cr:
 
345
            content = self.read('rU')
 
346
            return content.split('\n')
 
347
        else:
 
348
            f = self.open('rU')
 
349
            try:
 
350
                return f.readlines()
 
351
            finally:
 
352
                f.close()
 
353
 
 
354
    def load(self):
 
355
        """ return object unpickled from self.read() """
 
356
        f = self.open('rb')
 
357
        try:
 
358
            from cPickle import load
 
359
            return self._callex(load, f)
 
360
        finally:
 
361
            f.close()
 
362
 
 
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")
 
367
        try:
 
368
            self.rename(target)
 
369
        except py.error.EXDEV:  # invalid cross-device link
 
370
            self.copy(target)
 
371
            self.remove()
 
372
 
 
373
    def _getpymodule(self):
 
374
        """resolve this path to a module python object. """
 
375
        modname = str(self)
 
376
        modname = modname.replace('.', self.sep)
 
377
        try:
 
378
            return sys.modules[modname]
 
379
        except KeyError:
 
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
 
386
            try: 
 
387
                exec co in mod.__dict__
 
388
            except: 
 
389
                del sys.modules[modname] 
 
390
                raise 
 
391
            return mod
 
392
 
 
393
    def _getpycodeobj(self):
 
394
        """ read the path and compile it to a py.code.Code object. """
 
395
        s = self.read('rU')
 
396
        # XXX str(self) should show up somewhere in the code's filename
 
397
        return py.code.compile(s)
 
398
 
 
399
class PathStr(str):
 
400
    def __init__(self, path):
 
401
        global old_import_hook
 
402
        self.__path__ = path
 
403
        if old_import_hook is None:
 
404
            import __builtin__
 
405
            old_import_hook = __builtin__.__import__
 
406
            __builtin__.__import__ = custom_import_hook
 
407
 
 
408
def relativeimport(p, name, parent=None):
 
409
    names = name.split('.')
 
410
    last_list = [False] * (len(names)-1) + [True]
 
411
    modules = []
 
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)
 
417
        else:
 
418
            if is_last and p.new(basename=name+'.py').check():
 
419
                p = p.new(basename=name+'.py')
 
420
            else:
 
421
                p = p.new(basename=name).join('__init__.py')
 
422
                if not p.check():
 
423
                    return None   # not found
 
424
            submodule = p._getpymodule()
 
425
            if parent is not None:
 
426
                setattr(parent, name, submodule)
 
427
        modules.append(submodule)
 
428
        parent = submodule
 
429
    return modules   # success
 
430
 
 
431
 
 
432
old_import_hook = None
 
433
 
 
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
 
440
        modules = None
 
441
        if hasattr(__file__.__path__, 'pkgdir'):
 
442
            modules = relativeimport(__file__.__path__.pkgdir, name)
 
443
        if not modules:
 
444
            modules = relativeimport(__file__.__path__, name)
 
445
        if modules:
 
446
            if fromlist:
 
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)
 
454
                        # failures are fine
 
455
                return submodule
 
456
            else:
 
457
                return modules[0]   # outermost package
 
458
    # fall-back
 
459
    __tracebackhide__ = True 
 
460
    return old_import_hook(name, glob, loc, fromlist)
 
461