|
1 """File wrangling.""" |
|
2 |
|
3 import os, sys |
|
4 |
|
5 class FileLocator: |
|
6 """Understand how filenames work.""" |
|
7 |
|
8 def __init__(self): |
|
9 self.relative_dir = self.abs_file(os.curdir) + os.sep |
|
10 |
|
11 # Cache of results of calling the canonical_filename() method, to |
|
12 # avoid duplicating work. |
|
13 self.canonical_filename_cache = {} |
|
14 |
|
15 def abs_file(self, filename): |
|
16 """Return the absolute normalized form of `filename`.""" |
|
17 return os.path.normcase(os.path.abspath(os.path.realpath(filename))) |
|
18 |
|
19 def relative_filename(self, filename): |
|
20 """Return the relative form of `filename`. |
|
21 |
|
22 The filename will be relative to the current directory when the |
|
23 FileLocator was constructed. |
|
24 |
|
25 """ |
|
26 return filename.replace(self.relative_dir, "") |
|
27 |
|
28 def canonical_filename(self, filename): |
|
29 """Return a canonical filename for `filename`. |
|
30 |
|
31 An absolute path with no redundant components and normalized case. |
|
32 |
|
33 """ |
|
34 if filename not in self.canonical_filename_cache: |
|
35 f = filename |
|
36 if os.path.isabs(f) and not os.path.exists(f): |
|
37 if not self.get_zip_data(f): |
|
38 f = os.path.basename(f) |
|
39 if not os.path.isabs(f): |
|
40 for path in [os.curdir] + sys.path: |
|
41 g = os.path.join(path, f) |
|
42 if os.path.exists(g): |
|
43 f = g |
|
44 break |
|
45 cf = self.abs_file(f) |
|
46 self.canonical_filename_cache[filename] = cf |
|
47 return self.canonical_filename_cache[filename] |
|
48 |
|
49 def get_zip_data(self, filename): |
|
50 """Get data from `filename` if it is a zip file path. |
|
51 |
|
52 Returns the data read from the zip file, or None if no zip file could |
|
53 be found or `filename` isn't in it. |
|
54 |
|
55 """ |
|
56 import zipimport |
|
57 markers = ['.zip'+os.sep, '.egg'+os.sep] |
|
58 for marker in markers: |
|
59 if marker in filename: |
|
60 parts = filename.split(marker) |
|
61 try: |
|
62 zi = zipimport.zipimporter(parts[0]+marker[:-1]) |
|
63 except zipimport.ZipImportError: |
|
64 continue |
|
65 try: |
|
66 data = zi.get_data(parts[1]) |
|
67 except IOError: |
|
68 continue |
|
69 return data |
|
70 return None |