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