##// END OF EJS Templates
cygwin: add cygwin specific normcase logic...
FUJIWARA Katsunori -
r15711:c51c9dc1 default
parent child Browse files
Show More
@@ -1,419 +1,451
1 # posix.py - Posix utility function implementations for Mercurial
1 # posix.py - Posix utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
9 import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
10
10
11 posixfile = open
11 posixfile = open
12 nulldev = '/dev/null'
12 nulldev = '/dev/null'
13 normpath = os.path.normpath
13 normpath = os.path.normpath
14 samestat = os.path.samestat
14 samestat = os.path.samestat
15 oslink = os.link
15 oslink = os.link
16 unlink = os.unlink
16 unlink = os.unlink
17 rename = os.rename
17 rename = os.rename
18 expandglobs = False
18 expandglobs = False
19
19
20 umask = os.umask(0)
20 umask = os.umask(0)
21 os.umask(umask)
21 os.umask(umask)
22
22
23 def openhardlinks():
23 def openhardlinks():
24 '''return true if it is safe to hold open file handles to hardlinks'''
24 '''return true if it is safe to hold open file handles to hardlinks'''
25 return True
25 return True
26
26
27 def nlinks(name):
27 def nlinks(name):
28 '''return number of hardlinks for the given file'''
28 '''return number of hardlinks for the given file'''
29 return os.lstat(name).st_nlink
29 return os.lstat(name).st_nlink
30
30
31 def parsepatchoutput(output_line):
31 def parsepatchoutput(output_line):
32 """parses the output produced by patch and returns the filename"""
32 """parses the output produced by patch and returns the filename"""
33 pf = output_line[14:]
33 pf = output_line[14:]
34 if os.sys.platform == 'OpenVMS':
34 if os.sys.platform == 'OpenVMS':
35 if pf[0] == '`':
35 if pf[0] == '`':
36 pf = pf[1:-1] # Remove the quotes
36 pf = pf[1:-1] # Remove the quotes
37 else:
37 else:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
39 pf = pf[1:-1] # Remove the quotes
39 pf = pf[1:-1] # Remove the quotes
40 return pf
40 return pf
41
41
42 def sshargs(sshcmd, host, user, port):
42 def sshargs(sshcmd, host, user, port):
43 '''Build argument list for ssh'''
43 '''Build argument list for ssh'''
44 args = user and ("%s@%s" % (user, host)) or host
44 args = user and ("%s@%s" % (user, host)) or host
45 return port and ("%s -p %s" % (args, port)) or args
45 return port and ("%s -p %s" % (args, port)) or args
46
46
47 def isexec(f):
47 def isexec(f):
48 """check whether a file is executable"""
48 """check whether a file is executable"""
49 return (os.lstat(f).st_mode & 0100 != 0)
49 return (os.lstat(f).st_mode & 0100 != 0)
50
50
51 def setflags(f, l, x):
51 def setflags(f, l, x):
52 s = os.lstat(f).st_mode
52 s = os.lstat(f).st_mode
53 if l:
53 if l:
54 if not stat.S_ISLNK(s):
54 if not stat.S_ISLNK(s):
55 # switch file to link
55 # switch file to link
56 fp = open(f)
56 fp = open(f)
57 data = fp.read()
57 data = fp.read()
58 fp.close()
58 fp.close()
59 os.unlink(f)
59 os.unlink(f)
60 try:
60 try:
61 os.symlink(data, f)
61 os.symlink(data, f)
62 except OSError:
62 except OSError:
63 # failed to make a link, rewrite file
63 # failed to make a link, rewrite file
64 fp = open(f, "w")
64 fp = open(f, "w")
65 fp.write(data)
65 fp.write(data)
66 fp.close()
66 fp.close()
67 # no chmod needed at this point
67 # no chmod needed at this point
68 return
68 return
69 if stat.S_ISLNK(s):
69 if stat.S_ISLNK(s):
70 # switch link to file
70 # switch link to file
71 data = os.readlink(f)
71 data = os.readlink(f)
72 os.unlink(f)
72 os.unlink(f)
73 fp = open(f, "w")
73 fp = open(f, "w")
74 fp.write(data)
74 fp.write(data)
75 fp.close()
75 fp.close()
76 s = 0666 & ~umask # avoid restatting for chmod
76 s = 0666 & ~umask # avoid restatting for chmod
77
77
78 sx = s & 0100
78 sx = s & 0100
79 if x and not sx:
79 if x and not sx:
80 # Turn on +x for every +r bit when making a file executable
80 # Turn on +x for every +r bit when making a file executable
81 # and obey umask.
81 # and obey umask.
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
83 elif not x and sx:
83 elif not x and sx:
84 # Turn off all +x bits
84 # Turn off all +x bits
85 os.chmod(f, s & 0666)
85 os.chmod(f, s & 0666)
86
86
87 def copymode(src, dst, mode=None):
87 def copymode(src, dst, mode=None):
88 '''Copy the file mode from the file at path src to dst.
88 '''Copy the file mode from the file at path src to dst.
89 If src doesn't exist, we're using mode instead. If mode is None, we're
89 If src doesn't exist, we're using mode instead. If mode is None, we're
90 using umask.'''
90 using umask.'''
91 try:
91 try:
92 st_mode = os.lstat(src).st_mode & 0777
92 st_mode = os.lstat(src).st_mode & 0777
93 except OSError, inst:
93 except OSError, inst:
94 if inst.errno != errno.ENOENT:
94 if inst.errno != errno.ENOENT:
95 raise
95 raise
96 st_mode = mode
96 st_mode = mode
97 if st_mode is None:
97 if st_mode is None:
98 st_mode = ~umask
98 st_mode = ~umask
99 st_mode &= 0666
99 st_mode &= 0666
100 os.chmod(dst, st_mode)
100 os.chmod(dst, st_mode)
101
101
102 def checkexec(path):
102 def checkexec(path):
103 """
103 """
104 Check whether the given path is on a filesystem with UNIX-like exec flags
104 Check whether the given path is on a filesystem with UNIX-like exec flags
105
105
106 Requires a directory (like /foo/.hg)
106 Requires a directory (like /foo/.hg)
107 """
107 """
108
108
109 # VFAT on some Linux versions can flip mode but it doesn't persist
109 # VFAT on some Linux versions can flip mode but it doesn't persist
110 # a FS remount. Frequently we can detect it if files are created
110 # a FS remount. Frequently we can detect it if files are created
111 # with exec bit on.
111 # with exec bit on.
112
112
113 try:
113 try:
114 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
114 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
115 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
115 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
116 try:
116 try:
117 os.close(fh)
117 os.close(fh)
118 m = os.stat(fn).st_mode & 0777
118 m = os.stat(fn).st_mode & 0777
119 new_file_has_exec = m & EXECFLAGS
119 new_file_has_exec = m & EXECFLAGS
120 os.chmod(fn, m ^ EXECFLAGS)
120 os.chmod(fn, m ^ EXECFLAGS)
121 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
121 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
122 finally:
122 finally:
123 os.unlink(fn)
123 os.unlink(fn)
124 except (IOError, OSError):
124 except (IOError, OSError):
125 # we don't care, the user probably won't be able to commit anyway
125 # we don't care, the user probably won't be able to commit anyway
126 return False
126 return False
127 return not (new_file_has_exec or exec_flags_cannot_flip)
127 return not (new_file_has_exec or exec_flags_cannot_flip)
128
128
129 def checklink(path):
129 def checklink(path):
130 """check whether the given path is on a symlink-capable filesystem"""
130 """check whether the given path is on a symlink-capable filesystem"""
131 # mktemp is not racy because symlink creation will fail if the
131 # mktemp is not racy because symlink creation will fail if the
132 # file already exists
132 # file already exists
133 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
133 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
134 try:
134 try:
135 os.symlink(".", name)
135 os.symlink(".", name)
136 os.unlink(name)
136 os.unlink(name)
137 return True
137 return True
138 except (OSError, AttributeError):
138 except (OSError, AttributeError):
139 return False
139 return False
140
140
141 def checkosfilename(path):
141 def checkosfilename(path):
142 '''Check that the base-relative path is a valid filename on this platform.
142 '''Check that the base-relative path is a valid filename on this platform.
143 Returns None if the path is ok, or a UI string describing the problem.'''
143 Returns None if the path is ok, or a UI string describing the problem.'''
144 pass # on posix platforms, every path is ok
144 pass # on posix platforms, every path is ok
145
145
146 def setbinary(fd):
146 def setbinary(fd):
147 pass
147 pass
148
148
149 def pconvert(path):
149 def pconvert(path):
150 return path
150 return path
151
151
152 def localpath(path):
152 def localpath(path):
153 return path
153 return path
154
154
155 def samefile(fpath1, fpath2):
155 def samefile(fpath1, fpath2):
156 """Returns whether path1 and path2 refer to the same file. This is only
156 """Returns whether path1 and path2 refer to the same file. This is only
157 guaranteed to work for files, not directories."""
157 guaranteed to work for files, not directories."""
158 return os.path.samefile(fpath1, fpath2)
158 return os.path.samefile(fpath1, fpath2)
159
159
160 def samedevice(fpath1, fpath2):
160 def samedevice(fpath1, fpath2):
161 """Returns whether fpath1 and fpath2 are on the same device. This is only
161 """Returns whether fpath1 and fpath2 are on the same device. This is only
162 guaranteed to work for files, not directories."""
162 guaranteed to work for files, not directories."""
163 st1 = os.lstat(fpath1)
163 st1 = os.lstat(fpath1)
164 st2 = os.lstat(fpath2)
164 st2 = os.lstat(fpath2)
165 return st1.st_dev == st2.st_dev
165 return st1.st_dev == st2.st_dev
166
166
167 encodinglower = None
167 encodinglower = None
168 encodingupper = None
168 encodingupper = None
169
169
170 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
170 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
171 def normcase(path):
171 def normcase(path):
172 return path.lower()
172 return path.lower()
173
173
174 if sys.platform == 'darwin':
174 if sys.platform == 'darwin':
175 import fcntl # only needed on darwin, missing on jython
175 import fcntl # only needed on darwin, missing on jython
176
176
177 def normcase(path):
177 def normcase(path):
178 try:
178 try:
179 u = path.decode('utf-8')
179 u = path.decode('utf-8')
180 except UnicodeDecodeError:
180 except UnicodeDecodeError:
181 # percent-encode any characters that don't round-trip
181 # percent-encode any characters that don't round-trip
182 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
182 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
183 s = ""
183 s = ""
184 pos = 0
184 pos = 0
185 for c in path:
185 for c in path:
186 if p2[pos:pos + 1] == c:
186 if p2[pos:pos + 1] == c:
187 s += c
187 s += c
188 pos += 1
188 pos += 1
189 else:
189 else:
190 s += "%%%02X" % ord(c)
190 s += "%%%02X" % ord(c)
191 u = s.decode('utf-8')
191 u = s.decode('utf-8')
192
192
193 # Decompose then lowercase (HFS+ technote specifies lower)
193 # Decompose then lowercase (HFS+ technote specifies lower)
194 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
194 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
195
195
196 def realpath(path):
196 def realpath(path):
197 '''
197 '''
198 Returns the true, canonical file system path equivalent to the given
198 Returns the true, canonical file system path equivalent to the given
199 path.
199 path.
200
200
201 Equivalent means, in this case, resulting in the same, unique
201 Equivalent means, in this case, resulting in the same, unique
202 file system link to the path. Every file system entry, whether a file,
202 file system link to the path. Every file system entry, whether a file,
203 directory, hard link or symbolic link or special, will have a single
203 directory, hard link or symbolic link or special, will have a single
204 path preferred by the system, but may allow multiple, differing path
204 path preferred by the system, but may allow multiple, differing path
205 lookups to point to it.
205 lookups to point to it.
206
206
207 Most regular UNIX file systems only allow a file system entry to be
207 Most regular UNIX file systems only allow a file system entry to be
208 looked up by its distinct path. Obviously, this does not apply to case
208 looked up by its distinct path. Obviously, this does not apply to case
209 insensitive file systems, whether case preserving or not. The most
209 insensitive file systems, whether case preserving or not. The most
210 complex issue to deal with is file systems transparently reencoding the
210 complex issue to deal with is file systems transparently reencoding the
211 path, such as the non-standard Unicode normalisation required for HFS+
211 path, such as the non-standard Unicode normalisation required for HFS+
212 and HFSX.
212 and HFSX.
213 '''
213 '''
214 # Constants copied from /usr/include/sys/fcntl.h
214 # Constants copied from /usr/include/sys/fcntl.h
215 F_GETPATH = 50
215 F_GETPATH = 50
216 O_SYMLINK = 0x200000
216 O_SYMLINK = 0x200000
217
217
218 try:
218 try:
219 fd = os.open(path, O_SYMLINK)
219 fd = os.open(path, O_SYMLINK)
220 except OSError, err:
220 except OSError, err:
221 if err.errno == errno.ENOENT:
221 if err.errno == errno.ENOENT:
222 return path
222 return path
223 raise
223 raise
224
224
225 try:
225 try:
226 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
226 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
227 finally:
227 finally:
228 os.close(fd)
228 os.close(fd)
229 elif sys.version_info < (2, 4, 2, 'final'):
229 elif sys.version_info < (2, 4, 2, 'final'):
230 # Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
230 # Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
231 # didn't resolve symlinks that were the first component of the path.)
231 # didn't resolve symlinks that were the first component of the path.)
232 def realpath(path):
232 def realpath(path):
233 if os.path.isabs(path):
233 if os.path.isabs(path):
234 return os.path.realpath(path)
234 return os.path.realpath(path)
235 else:
235 else:
236 return os.path.realpath('./' + path)
236 return os.path.realpath('./' + path)
237 else:
237 else:
238 # Fallback to the likely inadequate Python builtin function.
238 # Fallback to the likely inadequate Python builtin function.
239 realpath = os.path.realpath
239 realpath = os.path.realpath
240
240
241 if sys.platform == 'cygwin':
242 # workaround for cygwin, in which mount point part of path is
243 # treated as case sensitive, even though underlying NTFS is case
244 # insensitive.
245
246 # default mount points
247 cygwinmountpoints = sorted([
248 "/usr/bin",
249 "/usr/lib",
250 "/cygdrive",
251 ], reverse=True)
252
253 # use upper-ing as normcase as same as NTFS workaround
254 def normcase(path):
255 pathlen = len(path)
256 if (pathlen == 0) or (path[0] != os.sep):
257 # treat as relative
258 return encodingupper(path)
259
260 # to preserve case of mountpoint part
261 for mp in cygwinmountpoints:
262 if not path.startswith(mp):
263 continue
264
265 mplen = len(mp)
266 if mplen == pathlen: # mount point itself
267 return mp
268 if path[mplen] == os.sep:
269 return mp + encodingupper(path[mplen:])
270
271 return encodingupper(path)
272
241 def shellquote(s):
273 def shellquote(s):
242 if os.sys.platform == 'OpenVMS':
274 if os.sys.platform == 'OpenVMS':
243 return '"%s"' % s
275 return '"%s"' % s
244 else:
276 else:
245 return "'%s'" % s.replace("'", "'\\''")
277 return "'%s'" % s.replace("'", "'\\''")
246
278
247 def quotecommand(cmd):
279 def quotecommand(cmd):
248 return cmd
280 return cmd
249
281
250 def popen(command, mode='r'):
282 def popen(command, mode='r'):
251 return os.popen(command, mode)
283 return os.popen(command, mode)
252
284
253 def testpid(pid):
285 def testpid(pid):
254 '''return False if pid dead, True if running or not sure'''
286 '''return False if pid dead, True if running or not sure'''
255 if os.sys.platform == 'OpenVMS':
287 if os.sys.platform == 'OpenVMS':
256 return True
288 return True
257 try:
289 try:
258 os.kill(pid, 0)
290 os.kill(pid, 0)
259 return True
291 return True
260 except OSError, inst:
292 except OSError, inst:
261 return inst.errno != errno.ESRCH
293 return inst.errno != errno.ESRCH
262
294
263 def explainexit(code):
295 def explainexit(code):
264 """return a 2-tuple (desc, code) describing a subprocess status
296 """return a 2-tuple (desc, code) describing a subprocess status
265 (codes from kill are negative - not os.system/wait encoding)"""
297 (codes from kill are negative - not os.system/wait encoding)"""
266 if code >= 0:
298 if code >= 0:
267 return _("exited with status %d") % code, code
299 return _("exited with status %d") % code, code
268 return _("killed by signal %d") % -code, -code
300 return _("killed by signal %d") % -code, -code
269
301
270 def isowner(st):
302 def isowner(st):
271 """Return True if the stat object st is from the current user."""
303 """Return True if the stat object st is from the current user."""
272 return st.st_uid == os.getuid()
304 return st.st_uid == os.getuid()
273
305
274 def findexe(command):
306 def findexe(command):
275 '''Find executable for command searching like which does.
307 '''Find executable for command searching like which does.
276 If command is a basename then PATH is searched for command.
308 If command is a basename then PATH is searched for command.
277 PATH isn't searched if command is an absolute or relative path.
309 PATH isn't searched if command is an absolute or relative path.
278 If command isn't found None is returned.'''
310 If command isn't found None is returned.'''
279 if sys.platform == 'OpenVMS':
311 if sys.platform == 'OpenVMS':
280 return command
312 return command
281
313
282 def findexisting(executable):
314 def findexisting(executable):
283 'Will return executable if existing file'
315 'Will return executable if existing file'
284 if os.path.isfile(executable) and os.access(executable, os.X_OK):
316 if os.path.isfile(executable) and os.access(executable, os.X_OK):
285 return executable
317 return executable
286 return None
318 return None
287
319
288 if os.sep in command:
320 if os.sep in command:
289 return findexisting(command)
321 return findexisting(command)
290
322
291 for path in os.environ.get('PATH', '').split(os.pathsep):
323 for path in os.environ.get('PATH', '').split(os.pathsep):
292 executable = findexisting(os.path.join(path, command))
324 executable = findexisting(os.path.join(path, command))
293 if executable is not None:
325 if executable is not None:
294 return executable
326 return executable
295 return None
327 return None
296
328
297 def setsignalhandler():
329 def setsignalhandler():
298 pass
330 pass
299
331
300 def statfiles(files):
332 def statfiles(files):
301 'Stat each file in files and yield stat or None if file does not exist.'
333 'Stat each file in files and yield stat or None if file does not exist.'
302 lstat = os.lstat
334 lstat = os.lstat
303 for nf in files:
335 for nf in files:
304 try:
336 try:
305 st = lstat(nf)
337 st = lstat(nf)
306 except OSError, err:
338 except OSError, err:
307 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
339 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
308 raise
340 raise
309 st = None
341 st = None
310 yield st
342 yield st
311
343
312 def getuser():
344 def getuser():
313 '''return name of current user'''
345 '''return name of current user'''
314 return getpass.getuser()
346 return getpass.getuser()
315
347
316 def username(uid=None):
348 def username(uid=None):
317 """Return the name of the user with the given uid.
349 """Return the name of the user with the given uid.
318
350
319 If uid is None, return the name of the current user."""
351 If uid is None, return the name of the current user."""
320
352
321 if uid is None:
353 if uid is None:
322 uid = os.getuid()
354 uid = os.getuid()
323 try:
355 try:
324 return pwd.getpwuid(uid)[0]
356 return pwd.getpwuid(uid)[0]
325 except KeyError:
357 except KeyError:
326 return str(uid)
358 return str(uid)
327
359
328 def groupname(gid=None):
360 def groupname(gid=None):
329 """Return the name of the group with the given gid.
361 """Return the name of the group with the given gid.
330
362
331 If gid is None, return the name of the current group."""
363 If gid is None, return the name of the current group."""
332
364
333 if gid is None:
365 if gid is None:
334 gid = os.getgid()
366 gid = os.getgid()
335 try:
367 try:
336 return grp.getgrgid(gid)[0]
368 return grp.getgrgid(gid)[0]
337 except KeyError:
369 except KeyError:
338 return str(gid)
370 return str(gid)
339
371
340 def groupmembers(name):
372 def groupmembers(name):
341 """Return the list of members of the group with the given
373 """Return the list of members of the group with the given
342 name, KeyError if the group does not exist.
374 name, KeyError if the group does not exist.
343 """
375 """
344 return list(grp.getgrnam(name).gr_mem)
376 return list(grp.getgrnam(name).gr_mem)
345
377
346 def spawndetached(args):
378 def spawndetached(args):
347 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
379 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
348 args[0], args)
380 args[0], args)
349
381
350 def gethgcmd():
382 def gethgcmd():
351 return sys.argv[:1]
383 return sys.argv[:1]
352
384
353 def termwidth():
385 def termwidth():
354 try:
386 try:
355 import termios, array, fcntl
387 import termios, array, fcntl
356 for dev in (sys.stderr, sys.stdout, sys.stdin):
388 for dev in (sys.stderr, sys.stdout, sys.stdin):
357 try:
389 try:
358 try:
390 try:
359 fd = dev.fileno()
391 fd = dev.fileno()
360 except AttributeError:
392 except AttributeError:
361 continue
393 continue
362 if not os.isatty(fd):
394 if not os.isatty(fd):
363 continue
395 continue
364 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
396 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
365 width = array.array('h', arri)[1]
397 width = array.array('h', arri)[1]
366 if width > 0:
398 if width > 0:
367 return width
399 return width
368 except ValueError:
400 except ValueError:
369 pass
401 pass
370 except IOError, e:
402 except IOError, e:
371 if e[0] == errno.EINVAL:
403 if e[0] == errno.EINVAL:
372 pass
404 pass
373 else:
405 else:
374 raise
406 raise
375 except ImportError:
407 except ImportError:
376 pass
408 pass
377 return 80
409 return 80
378
410
379 def makedir(path, notindexed):
411 def makedir(path, notindexed):
380 os.mkdir(path)
412 os.mkdir(path)
381
413
382 def unlinkpath(f):
414 def unlinkpath(f):
383 """unlink and remove the directory if it is empty"""
415 """unlink and remove the directory if it is empty"""
384 os.unlink(f)
416 os.unlink(f)
385 # try removing directories that might now be empty
417 # try removing directories that might now be empty
386 try:
418 try:
387 os.removedirs(os.path.dirname(f))
419 os.removedirs(os.path.dirname(f))
388 except OSError:
420 except OSError:
389 pass
421 pass
390
422
391 def lookupreg(key, name=None, scope=None):
423 def lookupreg(key, name=None, scope=None):
392 return None
424 return None
393
425
394 def hidewindow():
426 def hidewindow():
395 """Hide current shell window.
427 """Hide current shell window.
396
428
397 Used to hide the window opened when starting asynchronous
429 Used to hide the window opened when starting asynchronous
398 child process under Windows, unneeded on other systems.
430 child process under Windows, unneeded on other systems.
399 """
431 """
400 pass
432 pass
401
433
402 class cachestat(object):
434 class cachestat(object):
403 def __init__(self, path):
435 def __init__(self, path):
404 self.stat = os.stat(path)
436 self.stat = os.stat(path)
405
437
406 def cacheable(self):
438 def cacheable(self):
407 return bool(self.stat.st_ino)
439 return bool(self.stat.st_ino)
408
440
409 def __eq__(self, other):
441 def __eq__(self, other):
410 try:
442 try:
411 return self.stat == other.stat
443 return self.stat == other.stat
412 except AttributeError:
444 except AttributeError:
413 return False
445 return False
414
446
415 def __ne__(self, other):
447 def __ne__(self, other):
416 return not self == other
448 return not self == other
417
449
418 def executablepath():
450 def executablepath():
419 return None # available on Windows only
451 return None # available on Windows only
General Comments 0
You need to be logged in to leave comments. Login now