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