Completed
Push — master ( 8f3e72...b93e94 )
by Bjorn
59s
created

dkfileutils.Path.lexists()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 3
rs 10
1
# -*- coding: utf-8 -*-
2
"""Poor man's pathlib.
3
4
   (Path instances are subclasses of str, so interoperability with existing
5
   os.path code is greater than with Python 3's pathlib.)
6
"""
7
# pylint:disable=C0111,R0904
8
# R0904: too many public methods in Path
9
import os
10
import re
11
from contextlib import contextmanager
12
13
14
def doc(srcfn):
15
    def decorator(fn):
16
        fn.__doc__ = srcfn.__doc__.replace(srcfn.__name__, fn.__name__)
17
        return fn
18
    return decorator
19
20
21
class Path(str):
22
    """Poor man's pathlib.
23
    """
24
25
    def __div__(self, other):
26
        return Path(
27
            os.path.normcase(
28
                os.path.normpath(
29
                    os.path.join(self, other)
30
                )
31
            )
32
        )
33
34
    @doc(os.unlink)
35
    def unlink(self):
36
        os.unlink(self)
37
38
    def open(self, mode='r'):
39
        return open(self, mode)
40
41
    def __iter__(self):
42
        for root, dirs, files in os.walk(self):
43
            dotdirs = [d for d in dirs if d.startswith('.')]
44
            for d in dotdirs:
45
                dirs.remove(d)
46
            dotfiles = [d for d in files if d.startswith('.')]
47
            for d in dotfiles:
48
                files.remove(d)
49
            for fname in files:
50
                yield Path(os.path.join(root, fname))
51
52
    def __contains__(self, item):
53
        if self.isdir():
54
            return item in self.listdir()
55
        super(Path, self).__contains__(item)
56
57
    def glob(self, pat):
58
        """`pat` can be an extended glob pattern, e.g. `'**/*.less'`
59
           This code handles negations similarly to node.js' minimatch, i.e.
60
           a leading `!` will negate the entire pattern.
61
        """
62
        r = ""
63
        negate = int(pat.startswith('!'))
64
        i = negate
65
66
        while i < len(pat):
67
            if pat[i:i + 3] == '**/':
68
                r += "(?:.*/)?"
69
                i += 3
70
            elif pat[i] == "*":
71
                r += "[^/]*"
72
                i += 1
73
            elif pat[i] == ".":
74
                r += "[.]"
75
                i += 1
76
            elif pat[i] == "?":
77
                r += "."
78
                i += 1
79
            else:
80
                r += pat[i]
81
                i += 1
82
        r += r'\Z(?ms)'
83
        # print '\n\npat', pat
84
        # print 'regex:', r
85
        # print [s.relpath(self).replace('\\', '/') for s in self]
86
        rx = re.compile(r)
87
88
        def match(d):
89
            m = rx.match(d)
90
            return not m if negate else m
91
92
        return [s for s in self if match(s.relpath(self).replace('\\', '/'))]
93
94
    @doc(os.path.abspath)
95
    def abspath(self):
96
        return Path(os.path.abspath(self))
97
98
    def drive(self):
99
        """Return the drive of `self`.
100
        """
101
        return self.splitdrive()[0]
102
103
    def drivepath(self):
104
        """The path local to this drive (i.e. remove drive letter).
105
        """
106
        return self.splitdrive()[1]
107
108
    @doc(os.path.basename)
109
    def basename(self):
110
        return Path(os.path.basename(self))
111
112
    @doc(os.path.commonprefix)
113
    def commonprefix(self, *args):
114
        return os.path.commonprefix([str(self)] + [str(a) for a in args])
115
116
    @doc(os.path.dirname)
117
    def dirname(self):
118
        return Path(os.path.dirname(self))
119
120
    @doc(os.path.exists)
121
    def exists(self):
122
        return os.path.exists(self)
123
124
    @doc(os.path.expanduser)
125
    def expanduser(self):
126
        return Path(os.path.expanduser(self))
127
128
    @doc(os.path.expandvars)
129
    def expandvars(self):
130
        return Path(os.path.expandvars(self))
131
132
    @doc(os.path.getatime)
133
    def getatime(self):
134
        return os.path.getatime(self)
135
136
    @doc(os.path.getctime)
137
    def getctime(self):
138
        return os.path.getctime(self)
139
140
    @doc(os.path.getmtime)
141
    def getmtime(self):
142
        return os.path.getmtime(self)
143
144
    @doc(os.path.getsize)
145
    def getsize(self):
146
        return os.path.getsize(self)
147
148
    @doc(os.path.isabs)
149
    def isabs(self):
150
        return os.path.isabs(self)
151
152
    @doc(os.path.isdir)
153
    def isdir(self, *args, **kw):
154
        return os.path.isdir(self, *args, **kw)
155
156
    @doc(os.path.isfile)
157
    def isfile(self):
158
        return os.path.isfile(self)
159
160
    @doc(os.path.islink)
161
    def islink(self):
162
        return os.path.islink(self)
163
164
    @doc(os.path.ismount)
165
    def ismount(self):
166
        return os.path.ismount(self)
167
168
    @doc(os.path.join)
169
    def join(self, *args):
170
        return Path(os.path.join(self, *args))
171
172
    @doc(os.path.lexists)
173
    def lexists(self):
174
        return os.path.lexists(self)
175
176
    @doc(os.path.normcase)
177
    def normcase(self):
178
        return Path(os.path.normcase(self))
179
180
    @doc(os.path.normpath)
181
    def normpath(self):
182
        return Path(os.path.normpath(str(self)))
183
184
    @doc(os.path.realpath)
185
    def realpath(self):
186
        return Path(os.path.realpath(self))
187
188
    @doc(os.path.relpath)
189
    def relpath(self, other=""):
190
        return Path(os.path.relpath(str(self), str(other)))
191
192
    @doc(os.path.split)
193
    def split(self, sep=None, maxsplit=-1):
194
        # some heuristics to determine if this is a str.split call or
195
        # a os.split call...
196
        sval = str(self)
197
        if sep is not None or ' ' in sval:
198
            return sval.split(sep or ' ', maxsplit)
199
        return os.path.split(self)
200
201
    def parts(self):
202
        return re.split(r"\\|/", self)
203
204
    @doc(os.path.splitdrive)
205
    def splitdrive(self):
206
        drive, pth = os.path.splitdrive(self)
207
        return drive, Path(pth)
208
209
    @doc(os.path.splitext)
210
    def splitext(self):
211
        return os.path.splitext(self)
212
213
    @property
214
    def ext(self):
215
        return self.splitext()[1]
216
217
    if hasattr(os.path, 'splitunc'):  # pragma: nocover
218
        @doc(os.path.splitunc)
219
        def splitunc(self):
220
            return os.path.splitunc(self)
221
222
    @doc(os.access)
223
    def access(self, *args, **kw):
224
        return os.access(self, *args, **kw)
225
226
    @doc(os.chdir)
227
    def chdir(self):
228
        return os.chdir(self)
229
230
    @contextmanager
231
    def cd(self):
232
        cwd = os.getcwd()
233
        try:
234
            self.chdir()
235
            yield self
236
        finally:
237
            os.chdir(cwd)
238
239
    @doc(os.chmod)
240
    def chmod(self, *args, **kw):
241
        return os.chmod(self, *args, **kw)
242
243
    @doc(os.listdir)
244
    def listdir(self):
245
        return [Path(p) for p in os.listdir(self)]
246
247
    def list(self, filterfn=lambda x: True):
248
        """Return all direct descendands of directory `self` for which
249
           `filterfn` returns True.
250
        """
251
        return [self / p for p in self.listdir() if filterfn(self / p)]
252
253
    def subdirs(self):
254
        """Return all direct sub-directories.
255
        """
256
        return self.list(lambda p: p.isdir())
257
258
    def files(self):
259
        """Return all files in directory.
260
        """
261
        return self.list(lambda p: p.isfile())
262
263
    @doc(os.lstat)
264
    def lstat(self):
265
        return os.lstat(self)
266
267
    @doc(os.makedirs)
268
    def makedirs(self, path=None, mode=0777):
269
        pth = os.path.join(self, path) if path else self
270
        try:
271
            os.makedirs(pth, mode)
272
        except OSError:
273
            pass
274
        return Path(pth)
275
276
    @doc(os.mkdir)
277
    def mkdir(self, path, mode=0777):
278
        pth = os.path.join(self, path)
279
        os.mkdir(pth, mode)
280
        return Path(pth)
281
282
    @doc(os.remove)
283
    def remove(self):
284
        return os.remove(self)
285
286
    @doc(os.removedirs)
287
    def removedirs(self):
288
        return os.removedirs(self)
289
290
    @doc(os.rename)
291
    def rename(self, *args, **kw):
292
        return os.rename(self, *args, **kw)
293
294
    @doc(os.renames)
295
    def renames(self, *args, **kw):
296
        return os.renames(self, *args, **kw)
297
298
    @doc(os.rmdir)
299
    def rmdir(self):
300
        return os.rmdir(self)
301
302
    if hasattr(os, 'startfile'):  # pragma: nocover
303
        @doc(os.startfile)
304
        def startfile(self, *args, **kw):
305
            return os.startfile(self, *args, **kw)
306
307
    @doc(os.stat)
308
    def stat(self, *args, **kw):
309
        return os.stat(self, *args, **kw)
310
311
    @doc(os.utime)
312
    def utime(self, time=None):
313
        os.utime(self, time)
314
        return self.stat()
315
316
    def __add__(self, other):
317
        return Path(str(self) + str(other))
318