|
1 # |
|
2 # Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # This component and the accompanying materials are made available |
|
5 # under the terms of the License "Eclipse Public License v1.0" |
|
6 # which accompanies this distribution, and is available |
|
7 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 # |
|
9 # Initial Contributors: |
|
10 # Nokia Corporation - initial contribution. |
|
11 # |
|
12 # Contributors: |
|
13 # |
|
14 # Description: |
|
15 # generic_path module |
|
16 # |
|
17 |
|
18 import os |
|
19 import sys |
|
20 import re |
|
21 import types |
|
22 |
|
23 # are we on windows, and if so what is the current drive letter |
|
24 isWin = sys.platform.lower().startswith("win") |
|
25 if isWin: |
|
26 drive = re.match('^([A-Za-z]:)',os.getcwd()).group(0) |
|
27 |
|
28 # regex for "bare" drive letters |
|
29 driveRE = re.compile('^[A-Za-z]:$') |
|
30 |
|
31 # Base class |
|
32 |
|
33 class Path: |
|
34 """This class represents a file path. |
|
35 |
|
36 A generic path object supports operations without needing to know |
|
37 about Windows and Linux differences. The standard str() function can |
|
38 obtain a string version of the path in Local format for use by |
|
39 platform-specific functions (file opening for example). |
|
40 |
|
41 We use forward slashes as path separators (even on Windows). |
|
42 |
|
43 For example, |
|
44 |
|
45 path1 = generic_path.Path("/foo") |
|
46 path2 = generic_path.Path("bar", "bing.bang") |
|
47 |
|
48 print str(path1.Append(path2)) |
|
49 |
|
50 Prints /foo/bar/bing.bang on Linux |
|
51 Prints c:/foo/bar/bing.bang on Windows (if c is the current drive) |
|
52 """ |
|
53 |
|
54 def __init__(self, *arguments): |
|
55 """construct a path from a list of path elements""" |
|
56 |
|
57 if len(arguments) == 0: |
|
58 self.path = "" |
|
59 return |
|
60 |
|
61 list = [] |
|
62 for i,arg in enumerate(arguments): |
|
63 if isWin: |
|
64 if i == 0: |
|
65 # If the first element starts with \ or / then we will |
|
66 # add the current drive letter to make a fully absolute path |
|
67 if arg.startswith("\\\\"): |
|
68 list.append(arg) # A UNC path - don't mess with it |
|
69 elif arg.startswith("\\") or arg.startswith("/"): |
|
70 list.append(drive + arg) |
|
71 # If the first element is a bare drive then dress it with a \ |
|
72 # temporarily otherwise "join" will not work properly. |
|
73 elif driveRE.match(arg): |
|
74 list.append(arg + "\\") |
|
75 # nothing special about the first element |
|
76 else: |
|
77 list.append(arg) |
|
78 else: |
|
79 if arg.startswith("\\\\"): |
|
80 raise ValueError("non-initial path components must not start with \\\\ : %s" % arg) |
|
81 else: |
|
82 list.append(arg) |
|
83 if ";" in arg: |
|
84 raise ValueError("An individual windows Path may not contain ';' : %s" % arg) |
|
85 else: |
|
86 list.append(arg) |
|
87 |
|
88 self.path = os.path.join(*list) |
|
89 |
|
90 # normalise to avoid nastiness with dots and multiple separators |
|
91 # but do not normalise "" as it will become "." |
|
92 if self.path != "": |
|
93 self.path = os.path.normpath(self.path) |
|
94 |
|
95 # always use forward slashes as separators |
|
96 self.path = self.path.replace("\\", "/") |
|
97 |
|
98 # remove trailing slashes unless we are just / |
|
99 if self.path != "/": |
|
100 self.path = self.path.rstrip("/") |
|
101 |
|
102 def __str__(self): |
|
103 return self.path |
|
104 |
|
105 def GetNeutralStr(self): |
|
106 """return the path as a string that could be included in other paths.""" |
|
107 return self.path.replace(":","").replace("/","") |
|
108 |
|
109 def GetLocalString(self): |
|
110 """return a string in the local file-system format. |
|
111 |
|
112 e.g. C:/tmp on Windows or /C/tmp on Linux""" |
|
113 return self.path |
|
114 |
|
115 def isAbsolute(self): |
|
116 "test whether this path is absolute or relative" |
|
117 # C: is an absolute directory |
|
118 return (os.path.isabs(self.path) or driveRE.match(self.path)) |
|
119 |
|
120 def Absolute(self): |
|
121 """return an object for the absolute version of this path. |
|
122 |
|
123 Prepends the current working directory to relative paths and |
|
124 the current drive (on Windows) to /something type paths.""" |
|
125 # leave C: alone as abspath will stick the cwd on |
|
126 if driveRE.match(self.path): |
|
127 return Path(self.path) |
|
128 else: |
|
129 return Path(os.path.abspath(self.path)) |
|
130 |
|
131 def Append(self, *arguments): |
|
132 "return an object with path elements added at the end of this path" |
|
133 return Join(*((self,) + arguments)) |
|
134 |
|
135 def Prepend(self, *arguments): |
|
136 "return an object with path elements added at the start of this path" |
|
137 return Join(*(arguments + (self,))) |
|
138 |
|
139 def isDir(self): |
|
140 "test whether this path points to an existing directory" |
|
141 # C: is a directory |
|
142 return (os.path.isdir(self.path) or driveRE.match(self.path)) |
|
143 |
|
144 def isFile(self): |
|
145 "test whether this path points to an existing file" |
|
146 return os.path.isfile(self.path) |
|
147 |
|
148 def Exists(self): |
|
149 "test whether this path exists in the filesystem" |
|
150 if driveRE.match(self.path): |
|
151 return os.path.exists(self.path + "/") |
|
152 else: |
|
153 return os.path.exists(self.path) |
|
154 |
|
155 def Dir(self): |
|
156 "return an object for the directory part of this path" |
|
157 if driveRE.match(self.path): |
|
158 return Path(self.path) |
|
159 else: |
|
160 return Path(os.path.dirname(self.path)) |
|
161 |
|
162 def File(self): |
|
163 "return a string for the file part of this path" |
|
164 return os.path.basename(self.path) |
|
165 |
|
166 def Components(self): |
|
167 """return a list of the components of this path.""" |
|
168 return self.path.split('/') |
|
169 |
|
170 def FindCaseless(self): |
|
171 """Given a path which may not be not correct in terms of case, |
|
172 search the filesystem to find the corresponding, correct path. |
|
173 paths are assumed to be absolute and normalised (which they |
|
174 should be in this class). |
|
175 |
|
176 Assumes that the path is more right than wrong, i.e. starts |
|
177 with the full path and tests for existence - then takes the |
|
178 last component off and check for that. |
|
179 |
|
180 This will be inefficient if used in cases where the file |
|
181 has a high probability of not existing. |
|
182 """ |
|
183 |
|
184 if os.path.exists(self.path): |
|
185 return Path(self.path) |
|
186 |
|
187 unknown_elements = [] |
|
188 tail = self.path |
|
189 head = None |
|
190 while tail != '': |
|
191 if os.path.exists(tail): |
|
192 break |
|
193 else: |
|
194 (tail,head) = os.path.split(tail) |
|
195 #print "(head,tail) = (%s,%s)\n" % (head,tail) |
|
196 unknown_elements.append(head) |
|
197 |
|
198 if tail == None: |
|
199 result = "" |
|
200 else: |
|
201 result = tail |
|
202 |
|
203 # Now we know the bits that may be wrong so we can search for them |
|
204 unknown_elements.reverse() |
|
205 for item in unknown_elements: |
|
206 possible = os.path.join(result, item) |
|
207 if os.path.exists(possible): |
|
208 result = possible |
|
209 continue # not finished yet - only this element is ok |
|
210 |
|
211 # Nope, we really do have to search for this component of the path |
|
212 possible = None |
|
213 if result: |
|
214 for file in os.listdir(result): |
|
215 if file.lower() == item.lower(): |
|
216 possible = os.path.join(result,file) |
|
217 break # find first matching name (might not be right) |
|
218 if possible is None: |
|
219 result = "" |
|
220 break # really couldn't find the file |
|
221 result = possible |
|
222 |
|
223 if result == "": |
|
224 return None |
|
225 |
|
226 return Path(result) |
|
227 |
|
228 def From(self,source): |
|
229 """Returns the relative path from 'source' to here.""" |
|
230 list1 = source.Absolute().Components() |
|
231 list2 = self.Absolute().Components() |
|
232 |
|
233 # on windows if the drives are different |
|
234 # then the relative path is the absolute one. |
|
235 if isWin and list1[0] != list2[0]: |
|
236 return self.Absolute() |
|
237 |
|
238 final_list = [] |
|
239 for item in list1: |
|
240 if list2 != []: |
|
241 for widget in list2: |
|
242 if item == widget: |
|
243 list2.pop(0) |
|
244 break |
|
245 else: |
|
246 final_list.insert(0, "..") |
|
247 final_list.append(widget) |
|
248 list2.pop(0) |
|
249 break |
|
250 else: |
|
251 final_list.insert(0, "..") |
|
252 |
|
253 final_list.extend(list2) |
|
254 |
|
255 return Join(*final_list) |
|
256 |
|
257 def GetShellPath(self): |
|
258 """Returns correct slashes according to os type as a string |
|
259 """ |
|
260 if isWin: |
|
261 if "OSTYPE" in os.environ and os.environ['OSTYPE'] == "cygwin" : |
|
262 return self.path |
|
263 |
|
264 return self.path.replace("/", "\\") |
|
265 |
|
266 return self.path |
|
267 |
|
268 |
|
269 # Module functions |
|
270 |
|
271 def Join(*arguments): |
|
272 """Concatenate the given list to make a generic path object. |
|
273 |
|
274 This can accept both strings and Path objects, and join |
|
275 them "intelligently" to make a complete path.""" |
|
276 list = [] |
|
277 for arg in arguments: |
|
278 if isinstance(arg, Path): |
|
279 list.append(arg.path) |
|
280 else: |
|
281 list.append(arg) |
|
282 |
|
283 return Path(*list) |
|
284 |
|
285 def CurrentDir(): |
|
286 "return a Path object for the current working directory" |
|
287 return Path(os.getcwd()) |
|
288 |
|
289 def NormalisePathList(aList): |
|
290 """Convert a list of strings into a list of Path objects""" |
|
291 return map(lambda x: Path(x), aList) |
|
292 |
|
293 def Where(afile): |
|
294 """Return the location of a file 'afile' in the system path. |
|
295 |
|
296 On windows, adds .exe onto the filename if it's not there. Returns the first location it found or None if it wasn't found. |
|
297 |
|
298 >>> Where("python") |
|
299 "/usr/bin/python" |
|
300 >>> Where("nonexistentfile") |
|
301 None |
|
302 """ |
|
303 location = None |
|
304 if sys.platform.startswith("win"): |
|
305 if not afile.lower().endswith(".exe"): |
|
306 afile += ".exe" |
|
307 |
|
308 for current_file in [os.path.join(loop_number,afile) for loop_number in |
|
309 os.environ["PATH"].split(os.path.pathsep)]: |
|
310 if os.path.isfile(current_file): |
|
311 location = current_file |
|
312 break |
|
313 return location |
|
314 |
|
315 # end of generic_path module |