##// END OF EJS Templates
Check return value of all directory-finding functions is unicode....
Thomas Kluyver -
Show More
@@ -1,395 +1,401 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import os
18 18 import sys
19 19
20 20 import IPython
21 21 from IPython.utils.process import system
22 22 from IPython.utils.importstring import import_item
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Code
26 26 #-----------------------------------------------------------------------------
27 27
28 # in case filesystemencoding() returns None:
29 fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
28 fs_encoding = sys.getfilesystemencoding()
29
30 def _cast_unicode(s, enc=None):
31 """Turn 8-bit strings into unicode."""
32 if isinstance(s, bytes):
33 enc = enc or sys.getdefaultencoding()
34 return s.decode(enc)
35 return s
36
30 37
31 38 def _get_long_path_name(path):
32 39 """Dummy no-op."""
33 40 return path
34 41
35
36 42 if sys.platform == 'win32':
37 43 def _get_long_path_name(path):
38 44 """Get a long path name (expand ~) on Windows using ctypes.
39 45
40 46 Examples
41 47 --------
42 48
43 49 >>> get_long_path_name('c:\\docume~1')
44 50 u'c:\\\\Documents and Settings'
45 51
46 52 """
47 53 try:
48 54 import ctypes
49 55 except ImportError:
50 56 raise ImportError('you need to have ctypes installed for this to work')
51 57 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
52 58 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
53 59 ctypes.c_uint ]
54 60
55 61 buf = ctypes.create_unicode_buffer(260)
56 62 rv = _GetLongPathName(path, buf, 260)
57 63 if rv == 0 or rv > 260:
58 64 return path
59 65 else:
60 66 return buf.value
61 67
62 68
63 69 def get_long_path_name(path):
64 70 """Expand a path into its long form.
65 71
66 72 On Windows this expands any ~ in the paths. On other platforms, it is
67 73 a null operation.
68 74 """
69 75 return _get_long_path_name(path)
70 76
71 77
72 78 def get_py_filename(name):
73 79 """Return a valid python filename in the current directory.
74 80
75 81 If the given name is not a file, it adds '.py' and searches again.
76 82 Raises IOError with an informative message if the file isn't found."""
77 83
78 84 name = os.path.expanduser(name)
79 85 if not os.path.isfile(name) and not name.endswith('.py'):
80 86 name += '.py'
81 87 if os.path.isfile(name):
82 88 return name
83 89 else:
84 90 raise IOError,'File `%s` not found.' % name
85 91
86 92
87 93 def filefind(filename, path_dirs=None):
88 94 """Find a file by looking through a sequence of paths.
89 95
90 96 This iterates through a sequence of paths looking for a file and returns
91 97 the full, absolute path of the first occurence of the file. If no set of
92 98 path dirs is given, the filename is tested as is, after running through
93 99 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
94 100
95 101 filefind('myfile.txt')
96 102
97 103 will find the file in the current working dir, but::
98 104
99 105 filefind('~/myfile.txt')
100 106
101 107 Will find the file in the users home directory. This function does not
102 108 automatically try any paths, such as the cwd or the user's home directory.
103 109
104 110 Parameters
105 111 ----------
106 112 filename : str
107 113 The filename to look for.
108 114 path_dirs : str, None or sequence of str
109 115 The sequence of paths to look for the file in. If None, the filename
110 116 need to be absolute or be in the cwd. If a string, the string is
111 117 put into a sequence and the searched. If a sequence, walk through
112 118 each element and join with ``filename``, calling :func:`expandvars`
113 119 and :func:`expanduser` before testing for existence.
114 120
115 121 Returns
116 122 -------
117 123 Raises :exc:`IOError` or returns absolute path to file.
118 124 """
119 125
120 126 # If paths are quoted, abspath gets confused, strip them...
121 127 filename = filename.strip('"').strip("'")
122 128 # If the input is an absolute path, just check it exists
123 129 if os.path.isabs(filename) and os.path.isfile(filename):
124 130 return filename
125 131
126 132 if path_dirs is None:
127 133 path_dirs = ("",)
128 134 elif isinstance(path_dirs, basestring):
129 135 path_dirs = (path_dirs,)
130 136
131 137 for path in path_dirs:
132 138 if path == '.': path = os.getcwd()
133 139 testname = expand_path(os.path.join(path, filename))
134 140 if os.path.isfile(testname):
135 141 return os.path.abspath(testname)
136 142
137 143 raise IOError("File %r does not exist in any of the search paths: %r" %
138 144 (filename, path_dirs) )
139 145
140 146
141 147 class HomeDirError(Exception):
142 148 pass
143 149
144 150
145 151 def get_home_dir():
146 152 """Return the closest possible equivalent to a 'home' directory.
147 153
148 154 * On POSIX, we try $HOME.
149 155 * On Windows we try:
150 156 - %HOMESHARE%
151 157 - %HOMEDRIVE\%HOMEPATH%
152 158 - %USERPROFILE%
153 159 - Registry hack for My Documents
154 160 - %HOME%: rare, but some people with unix-like setups may have defined it
155 161 * On Dos C:\
156 162
157 163 Currently only Posix and NT are implemented, a HomeDirError exception is
158 164 raised for all other OSes.
159 165 """
160 166
161 167 isdir = os.path.isdir
162 168 env = os.environ
163 169
164 170 # first, check py2exe distribution root directory for _ipython.
165 171 # This overrides all. Normally does not exist.
166 172
167 173 if hasattr(sys, "frozen"): #Is frozen by py2exe
168 174 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
169 175 root, rest = IPython.__file__.lower().split('library.zip')
170 176 else:
171 177 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
172 178 root=os.path.abspath(root).rstrip('\\')
173 179 if isdir(os.path.join(root, '_ipython')):
174 180 os.environ["IPYKITROOT"] = root
175 return root.decode(fs_encoding)
181 return _cast_unicode(root, fs_encoding)
176 182
177 183 if os.name == 'posix':
178 184 # Linux, Unix, AIX, OS X
179 185 try:
180 186 homedir = env['HOME']
181 187 except KeyError:
182 188 # Last-ditch attempt at finding a suitable $HOME, on systems where
183 189 # it may not be defined in the environment but the system shell
184 190 # still knows it - reported once as:
185 191 # https://github.com/ipython/ipython/issues/154
186 192 from subprocess import Popen, PIPE
187 193 homedir = Popen('echo $HOME', shell=True,
188 194 stdout=PIPE).communicate()[0].strip()
189 195 if homedir:
190 return homedir.decode(fs_encoding)
196 return _cast_unicode(homedir, fs_encoding)
191 197 else:
192 198 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
193 199 else:
194 return homedir.decode(fs_encoding)
200 return _cast_unicode(homedir, fs_encoding)
195 201 elif os.name == 'nt':
196 202 # Now for win9x, XP, Vista, 7?
197 203 # For some strange reason all of these return 'nt' for os.name.
198 204 # First look for a network home directory. This will return the UNC
199 205 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
200 206 # is needed when running IPython on cluster where all paths have to
201 207 # be UNC.
202 208 try:
203 209 homedir = env['HOMESHARE']
204 210 except KeyError:
205 211 pass
206 212 else:
207 213 if isdir(homedir):
208 return homedir.decode(fs_encoding)
214 return _cast_unicode(homedir, fs_encoding)
209 215
210 216 # Now look for a local home directory
211 217 try:
212 218 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
213 219 except KeyError:
214 220 pass
215 221 else:
216 222 if isdir(homedir):
217 return homedir.decode(fs_encoding)
223 return _cast_unicode(homedir, fs_encoding)
218 224
219 225 # Now the users profile directory
220 226 try:
221 227 homedir = os.path.join(env['USERPROFILE'])
222 228 except KeyError:
223 229 pass
224 230 else:
225 231 if isdir(homedir):
226 return homedir.decode(fs_encoding)
232 return _cast_unicode(homedir, fs_encoding)
227 233
228 234 # Use the registry to get the 'My Documents' folder.
229 235 try:
230 236 import _winreg as wreg
231 237 key = wreg.OpenKey(
232 238 wreg.HKEY_CURRENT_USER,
233 239 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
234 240 )
235 241 homedir = wreg.QueryValueEx(key,'Personal')[0]
236 242 key.Close()
237 243 except:
238 244 pass
239 245 else:
240 246 if isdir(homedir):
241 return homedir.decode(fs_encoding)
247 return _cast_unicode(homedir, fs_encoding)
242 248
243 249 # A user with a lot of unix tools in win32 may have defined $HOME.
244 250 # Try this as a last ditch option.
245 251 try:
246 252 homedir = env['HOME']
247 253 except KeyError:
248 254 pass
249 255 else:
250 256 if isdir(homedir):
251 return homedir.decode(fs_encoding)
257 return _cast_unicode(homedir, fs_encoding)
252 258
253 259 # If all else fails, raise HomeDirError
254 260 raise HomeDirError('No valid home directory could be found')
255 261 elif os.name == 'dos':
256 262 # Desperate, may do absurd things in classic MacOS. May work under DOS.
257 return 'C:\\'.decode(fs_encoding)
263 return u'C:\\'
258 264 else:
259 265 raise HomeDirError('No valid home directory could be found for your OS')
260 266
261 267 def get_xdg_dir():
262 268 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
263 269
264 270 This is only for posix (Linux,Unix,OS X, etc) systems.
265 271 """
266 272
267 273 isdir = os.path.isdir
268 274 env = os.environ
269 275
270 276 if os.name == 'posix':
271 277 # Linux, Unix, AIX, OS X
272 278 # use ~/.config if not set OR empty
273 279 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
274 280 if xdg and isdir(xdg):
275 return xdg.decode(fs_encoding)
281 return _cast_unicode(xdg, fs_encoding)
276 282
277 283 return None
278 284
279 285
280 286 def get_ipython_dir():
281 287 """Get the IPython directory for this platform and user.
282 288
283 289 This uses the logic in `get_home_dir` to find the home directory
284 290 and the adds .ipython to the end of the path.
285 291 """
286 292
287 293 env = os.environ
288 294 pjoin = os.path.join
289 295 exists = os.path.exists
290 296
291 297 ipdir_def = '.ipython'
292 298 xdg_def = 'ipython'
293 299
294 300 home_dir = get_home_dir()
295 301 xdg_dir = get_xdg_dir()
296 302 # import pdb; pdb.set_trace() # dbg
297 303 ipdir = env.get('IPYTHON_DIR', env.get('IPYTHONDIR', None))
298 304 if ipdir is None:
299 305 # not set explicitly, use XDG_CONFIG_HOME or HOME
300 306 home_ipdir = pjoin(home_dir, ipdir_def)
301 307 if xdg_dir:
302 308 # use XDG, as long as the user isn't already
303 309 # using $HOME/.ipython and *not* XDG/ipython
304 310
305 311 xdg_ipdir = pjoin(xdg_dir, xdg_def)
306 312
307 313 if exists(xdg_ipdir) or not exists(home_ipdir):
308 314 ipdir = xdg_ipdir
309 315
310 316 if ipdir is None:
311 317 # not using XDG
312 318 ipdir = home_ipdir
313
314 return ipdir.decode(fs_encoding)
319
320 return _cast_unicode(ipdir, fs_encoding)
315 321
316 322
317 323 def get_ipython_package_dir():
318 324 """Get the base directory where IPython itself is installed."""
319 325 ipdir = os.path.dirname(IPython.__file__)
320 return ipdir.decode(fs_encoding)
326 return _cast_unicode(ipdir, fs_encoding)
321 327
322 328
323 329 def get_ipython_module_path(module_str):
324 330 """Find the path to an IPython module in this version of IPython.
325 331
326 332 This will always find the version of the module that is in this importable
327 333 IPython package. This will always return the path to the ``.py``
328 334 version of the module.
329 335 """
330 336 if module_str == 'IPython':
331 337 return os.path.join(get_ipython_package_dir(), '__init__.py')
332 338 mod = import_item(module_str)
333 339 the_path = mod.__file__.replace('.pyc', '.py')
334 340 the_path = the_path.replace('.pyo', '.py')
335 return the_path.decode(fs_encoding)
341 return _cast_unicode(the_path, fs_encoding)
336 342
337 343
338 344 def expand_path(s):
339 345 """Expand $VARS and ~names in a string, like a shell
340 346
341 347 :Examples:
342 348
343 349 In [2]: os.environ['FOO']='test'
344 350
345 351 In [3]: expand_path('variable FOO is $FOO')
346 352 Out[3]: 'variable FOO is test'
347 353 """
348 354 # This is a pretty subtle hack. When expand user is given a UNC path
349 355 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
350 356 # the $ to get (\\server\share\%username%). I think it considered $
351 357 # alone an empty var. But, we need the $ to remains there (it indicates
352 358 # a hidden share).
353 359 if os.name=='nt':
354 360 s = s.replace('$\\', 'IPYTHON_TEMP')
355 361 s = os.path.expandvars(os.path.expanduser(s))
356 362 if os.name=='nt':
357 363 s = s.replace('IPYTHON_TEMP', '$\\')
358 364 return s
359 365
360 366
361 367 def target_outdated(target,deps):
362 368 """Determine whether a target is out of date.
363 369
364 370 target_outdated(target,deps) -> 1/0
365 371
366 372 deps: list of filenames which MUST exist.
367 373 target: single filename which may or may not exist.
368 374
369 375 If target doesn't exist or is older than any file listed in deps, return
370 376 true, otherwise return false.
371 377 """
372 378 try:
373 379 target_time = os.path.getmtime(target)
374 380 except os.error:
375 381 return 1
376 382 for dep in deps:
377 383 dep_time = os.path.getmtime(dep)
378 384 if dep_time > target_time:
379 385 #print "For target",target,"Dep failed:",dep # dbg
380 386 #print "times (dep,tar):",dep_time,target_time # dbg
381 387 return 1
382 388 return 0
383 389
384 390
385 391 def target_update(target,deps,cmd):
386 392 """Update a target with a given command given a list of dependencies.
387 393
388 394 target_update(target,deps,cmd) -> runs cmd if target is outdated.
389 395
390 396 This is just a wrapper around target_outdated() which calls the given
391 397 command if target is outdated."""
392 398
393 399 if target_outdated(target,deps):
394 400 system(cmd)
395 401
General Comments 0
You need to be logged in to leave comments. Login now