1 |
# Copyright (c) 2001, 2002 by Intevation GmbH |
2 |
# Authors: |
3 |
# Bernhard Herzog <[email protected]> |
4 |
# |
5 |
# This program is free software under the GPL (>=v2) |
6 |
# Read the file COPYING coming with Thuban for details. |
7 |
|
8 |
""" |
9 |
Functions to deal with filenames |
10 |
""" |
11 |
|
12 |
__version__ = "$Revision$" |
13 |
|
14 |
import os |
15 |
import os.path |
16 |
from tempfile import mktemp |
17 |
|
18 |
from string import join |
19 |
|
20 |
from Thuban import _ |
21 |
|
22 |
def relative_filename_common(dir, absname, sep): |
23 |
"""Return a filename relative to dir for the absolute file name absname. |
24 |
This is part the posix and nt versions have in common. Both dir and |
25 |
absname are assumed to be normalized (as done with os.normpath) |
26 |
absolute filenames without drive letters. sep is the platform |
27 |
specific directory separator. |
28 |
""" |
29 |
|
30 |
# split the filenames into their components. remove the first item |
31 |
# since it will be always empty because both names are absolute. |
32 |
dir_parts = dir.split(sep)[1:] |
33 |
absname_parts = absname.split(sep)[1:] |
34 |
|
35 |
# count the number of common parts at the start of dir_parts and |
36 |
# absname_parts |
37 |
max_common = min(len(dir_parts), len(absname_parts)) |
38 |
common = 0 |
39 |
while common < max_common and dir_parts[common] == absname_parts[common]: |
40 |
common = common + 1 |
41 |
|
42 |
# If the common part is the root directory, return absname |
43 |
if common == 0: |
44 |
return absname |
45 |
|
46 |
# for each directory under the common part prepend a '..'. |
47 |
rel_parts = (len(dir_parts) - common) * ['..'] + absname_parts[common:] |
48 |
return join(rel_parts, sep) |
49 |
|
50 |
|
51 |
def relative_filename_posix(dir, absname): |
52 |
"""Return a filename relative to dir for the absolute file name absname. |
53 |
|
54 |
If absname is already a relative filename, return it unchanged. If |
55 |
the common directory of dir and absname is /, return absname |
56 |
unchanged. If dir is not an absolute name, raise TypeError. |
57 |
|
58 |
This is the posix specific version of relative_filename. |
59 |
|
60 |
Example: |
61 |
>>> from fileutil import relative_filename_posix |
62 |
>>> relative_filename_posix("/usr/local/lib/", "/usr/local/lib/python") |
63 |
'python' |
64 |
>>> relative_filename_posix("/usr/local/lib/", "/usr/local/bin/python") |
65 |
'../bin/python' |
66 |
>>> relative_filename_posix("/usr/local/lib/", "/usr/bin/python") |
67 |
'../../bin/python' |
68 |
>>> relative_filename_posix("/usr/local/lib/", "/var/spool/mail") |
69 |
'/var/spool/mail' |
70 |
>>> relative_filename_posix("/home/", "xyzzy") |
71 |
'xyzzy' |
72 |
>>> relative_filename_posix("home/", "/xyzzy") |
73 |
Traceback (most recent call last): |
74 |
File "<stdin>", line 1, in ? |
75 |
File "fileutil.py", line 42, in relative_filename_posix |
76 |
raise TypeError("first argument must be an absolute filename") |
77 |
TypeError: first argument must be an absolute filename |
78 |
""" |
79 |
# for posix, the common part does exactly what we need, except for |
80 |
# the special cases and input checking. Import posixpath explicitly |
81 |
# for that to faciliate testing |
82 |
import posixpath |
83 |
if not posixpath.isabs(absname): |
84 |
return absname |
85 |
|
86 |
if not posixpath.isabs(dir): |
87 |
raise TypeError(_("first argument must be an absolute filename")) |
88 |
|
89 |
dir = posixpath.normpath(dir) |
90 |
absname = posixpath.normpath(absname) |
91 |
|
92 |
return relative_filename_common(dir, absname, "/") |
93 |
|
94 |
def relative_filename_nt(dir, absname): |
95 |
r"""Return a filename relative to dir for the absolute file name absname. |
96 |
|
97 |
If absname is already a relative filename or if dir and absname are |
98 |
on different drives, return absname. If the common directory of dir |
99 |
and absname is the drive's root directory, return absname. If dir is |
100 |
not an absolute name or either name doesn't have a drive letter, |
101 |
raise TypeError. |
102 |
|
103 |
This is the nt specific version of relative_filename. |
104 |
|
105 |
Example: |
106 |
>>> from fileutil import relative_filename_nt |
107 |
>>> relative_filename_nt(r"C:\Programme\Python", r"C:\Programme\Thuban") |
108 |
'..\\Thuban' |
109 |
>>> relative_filename_nt(r"C:\Programme\Python", r"D:\Programme\Thuban") |
110 |
'D:\\Programme\\Thuban' |
111 |
>>> relative_filename_nt(r"C:\Programme\Python", r"C:Programme") |
112 |
'C:Programme' |
113 |
>>> relative_filename_nt(r"C:Programme\Python", r"C:Programme") |
114 |
Traceback (most recent call last): |
115 |
File "<stdin>", line 1, in ? |
116 |
File "fileutil.py", line 123, in relative_filename_nt |
117 |
raise TypeError("first argument must be an absolute filename") |
118 |
TypeError: first argument must be an absolute filename |
119 |
>>> relative_filename_nt(r"\Programme\Python", r"\Programme") |
120 |
Traceback (most recent call last): |
121 |
File "<stdin>", line 1, in ? |
122 |
File "fileutil.py", line 120, in relative_filename_nt |
123 |
raise TypeError("Both parameters must have a drive letter") |
124 |
TypeError: Both parameters must have a drive letter |
125 |
""" |
126 |
# first check the parameters. Imort ntpath directly to facilitate |
127 |
# testing on non-nt systems. |
128 |
import ntpath |
129 |
|
130 |
dir = ntpath.normpath(dir) |
131 |
absname = ntpath.normpath(absname) |
132 |
|
133 |
dir_drive, dir_rest = ntpath.splitdrive(dir) |
134 |
absname_drive, absname_rest = ntpath.splitdrive(absname) |
135 |
#print dir_drive, dir_rest |
136 |
#print absname_drive, absname_rest |
137 |
if not dir_drive or not absname_drive: |
138 |
raise TypeError(_("Both parameters must have a drive letter")) |
139 |
|
140 |
if not ntpath.isabs(dir_rest): |
141 |
raise TypeError(_("first argument must be an absolute filename")) |
142 |
|
143 |
# handle some special cases |
144 |
if not ntpath.isabs(absname_rest): |
145 |
return absname |
146 |
|
147 |
if dir_drive != absname_drive: |
148 |
return absname |
149 |
|
150 |
# Now both dir_rest and absname_rest are absolute filenames without |
151 |
# drive letter. We can now use the common part to determine the |
152 |
# relative name |
153 |
return relative_filename_common(dir_rest, absname_rest, "\\") |
154 |
|
155 |
def get_application_dir(): |
156 |
"""Determine the path to the .thuban directory. Create the directory |
157 |
if it doesn't exist. |
158 |
|
159 |
Under posix systems use the os.expanduser() method. |
160 |
Under Win32 try to read the "Explorer/Shell Folders/" value "AppData". |
161 |
""" |
162 |
|
163 |
if os.name == 'posix': |
164 |
dir = os.path.expanduser("~/.thuban") |
165 |
if not os.path.isdir(dir): |
166 |
os.mkdir(dir) |
167 |
return dir |
168 |
elif os.name == 'nt': |
169 |
regkey = 1 |
170 |
try: |
171 |
import _winreg as wreg |
172 |
except ImportError: |
173 |
regkey = 0 |
174 |
|
175 |
if regkey: |
176 |
try: |
177 |
key = wreg.OpenKey(wreg.HKEY_CURRENT_USER, |
178 |
"Software\\Microsoft\\Windows\\CurrentVersion\\"\ |
179 |
"Explorer\\Shell Folders") |
180 |
dir = wreg.QueryValueEx(key, "AppData")[0] |
181 |
dir = os.path.join(dir, "thuban") |
182 |
except: |
183 |
regkey = 0 |
184 |
|
185 |
if not regkey: |
186 |
# The fallback. This should result in something like the |
187 |
# user directory ... |
188 |
guess = os.path.dirname( |
189 |
os.path.dirname(os.path.dirname(mktemp())) |
190 |
) |
191 |
dir = os.path.join(guess, "thuban") |
192 |
if not os.path.isdir(dir): |
193 |
os.mkdir(dir) |
194 |
|
195 |
return dir |
196 |
|
197 |
else: |
198 |
raise RuntimeError(_("No implementation of get_application_dir" |
199 |
" available for platform") + os.name) |
200 |
|
201 |
# bind the appropriate version of relative_filename for the platform |
202 |
# we're currently running on. |
203 |
if os.name == "posix": |
204 |
relative_filename = relative_filename_posix |
205 |
elif os.name == "nt": |
206 |
relative_filename = relative_filename_nt |
207 |
else: |
208 |
raise RuntimeError(_("No implementation of relative_filename" |
209 |
" available for platform") + os.name) |
210 |
|
211 |
__test__ = {"relative_filename_posix": relative_filename_posix, |
212 |
"relative_filename_nt": relative_filename_nt} |
213 |
|
214 |
# if run as a script, run doctest |
215 |
def _test(): |
216 |
import doctest, fileutil |
217 |
# Pass an isprivate function that always returns true so that only |
218 |
# items in __test__ are tested |
219 |
return doctest.testmod(fileutil, isprivate = lambda *args: 1) |
220 |
|
221 |
if __name__ == "__main__": |
222 |
_test() |