Completed
Push — master ( 74e424...6dac31 )
by Bjorn
49s
created

dkfileutils.Path.cd()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

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