Total Complexity | 83 |
Total Lines | 283 |
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(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 |