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