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 | 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 |