##// END OF EJS Templates
posix: replace superfluous pass statement with explicit return
Augie Fackler -
r34382:ab687e06 default
parent child Browse files
Show More
@@ -1,675 +1,675
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import fcntl
12 12 import getpass
13 13 import grp
14 14 import os
15 15 import pwd
16 16 import re
17 17 import select
18 18 import stat
19 19 import sys
20 20 import tempfile
21 21 import unicodedata
22 22
23 23 from .i18n import _
24 24 from . import (
25 25 encoding,
26 26 error,
27 27 pycompat,
28 28 )
29 29
30 30 posixfile = open
31 31 normpath = os.path.normpath
32 32 samestat = os.path.samestat
33 33 try:
34 34 oslink = os.link
35 35 except AttributeError:
36 36 # Some platforms build Python without os.link on systems that are
37 37 # vaguely unix-like but don't have hardlink support. For those
38 38 # poor souls, just say we tried and that it failed so we fall back
39 39 # to copies.
40 40 def oslink(src, dst):
41 41 raise OSError(errno.EINVAL,
42 42 'hardlinks not supported: %s to %s' % (src, dst))
43 43 unlink = os.unlink
44 44 rename = os.rename
45 45 removedirs = os.removedirs
46 46 expandglobs = False
47 47
48 48 umask = os.umask(0)
49 49 os.umask(umask)
50 50
51 51 def split(p):
52 52 '''Same as posixpath.split, but faster
53 53
54 54 >>> import posixpath
55 55 >>> for f in [b'/absolute/path/to/file',
56 56 ... b'relative/path/to/file',
57 57 ... b'file_alone',
58 58 ... b'path/to/directory/',
59 59 ... b'/multiple/path//separators',
60 60 ... b'/file_at_root',
61 61 ... b'///multiple_leading_separators_at_root',
62 62 ... b'']:
63 63 ... assert split(f) == posixpath.split(f), f
64 64 '''
65 65 ht = p.rsplit('/', 1)
66 66 if len(ht) == 1:
67 67 return '', p
68 68 nh = ht[0].rstrip('/')
69 69 if nh:
70 70 return nh, ht[1]
71 71 return ht[0] + '/', ht[1]
72 72
73 73 def openhardlinks():
74 74 '''return true if it is safe to hold open file handles to hardlinks'''
75 75 return True
76 76
77 77 def nlinks(name):
78 78 '''return number of hardlinks for the given file'''
79 79 return os.lstat(name).st_nlink
80 80
81 81 def parsepatchoutput(output_line):
82 82 """parses the output produced by patch and returns the filename"""
83 83 pf = output_line[14:]
84 84 if pycompat.sysplatform == 'OpenVMS':
85 85 if pf[0] == '`':
86 86 pf = pf[1:-1] # Remove the quotes
87 87 else:
88 88 if pf.startswith("'") and pf.endswith("'") and " " in pf:
89 89 pf = pf[1:-1] # Remove the quotes
90 90 return pf
91 91
92 92 def sshargs(sshcmd, host, user, port):
93 93 '''Build argument list for ssh'''
94 94 args = user and ("%s@%s" % (user, host)) or host
95 95 if '-' in args[:1]:
96 96 raise error.Abort(
97 97 _('illegal ssh hostname or username starting with -: %s') % args)
98 98 args = shellquote(args)
99 99 if port:
100 100 args = '-p %s %s' % (shellquote(port), args)
101 101 return args
102 102
103 103 def isexec(f):
104 104 """check whether a file is executable"""
105 105 return (os.lstat(f).st_mode & 0o100 != 0)
106 106
107 107 def setflags(f, l, x):
108 108 st = os.lstat(f)
109 109 s = st.st_mode
110 110 if l:
111 111 if not stat.S_ISLNK(s):
112 112 # switch file to link
113 113 fp = open(f)
114 114 data = fp.read()
115 115 fp.close()
116 116 unlink(f)
117 117 try:
118 118 os.symlink(data, f)
119 119 except OSError:
120 120 # failed to make a link, rewrite file
121 121 fp = open(f, "w")
122 122 fp.write(data)
123 123 fp.close()
124 124 # no chmod needed at this point
125 125 return
126 126 if stat.S_ISLNK(s):
127 127 # switch link to file
128 128 data = os.readlink(f)
129 129 unlink(f)
130 130 fp = open(f, "w")
131 131 fp.write(data)
132 132 fp.close()
133 133 s = 0o666 & ~umask # avoid restatting for chmod
134 134
135 135 sx = s & 0o100
136 136 if st.st_nlink > 1 and bool(x) != bool(sx):
137 137 # the file is a hardlink, break it
138 138 with open(f, "rb") as fp:
139 139 data = fp.read()
140 140 unlink(f)
141 141 with open(f, "wb") as fp:
142 142 fp.write(data)
143 143
144 144 if x and not sx:
145 145 # Turn on +x for every +r bit when making a file executable
146 146 # and obey umask.
147 147 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
148 148 elif not x and sx:
149 149 # Turn off all +x bits
150 150 os.chmod(f, s & 0o666)
151 151
152 152 def copymode(src, dst, mode=None):
153 153 '''Copy the file mode from the file at path src to dst.
154 154 If src doesn't exist, we're using mode instead. If mode is None, we're
155 155 using umask.'''
156 156 try:
157 157 st_mode = os.lstat(src).st_mode & 0o777
158 158 except OSError as inst:
159 159 if inst.errno != errno.ENOENT:
160 160 raise
161 161 st_mode = mode
162 162 if st_mode is None:
163 163 st_mode = ~umask
164 164 st_mode &= 0o666
165 165 os.chmod(dst, st_mode)
166 166
167 167 def checkexec(path):
168 168 """
169 169 Check whether the given path is on a filesystem with UNIX-like exec flags
170 170
171 171 Requires a directory (like /foo/.hg)
172 172 """
173 173
174 174 # VFAT on some Linux versions can flip mode but it doesn't persist
175 175 # a FS remount. Frequently we can detect it if files are created
176 176 # with exec bit on.
177 177
178 178 try:
179 179 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
180 180 cachedir = os.path.join(path, '.hg', 'cache')
181 181 if os.path.isdir(cachedir):
182 182 checkisexec = os.path.join(cachedir, 'checkisexec')
183 183 checknoexec = os.path.join(cachedir, 'checknoexec')
184 184
185 185 try:
186 186 m = os.stat(checkisexec).st_mode
187 187 except OSError as e:
188 188 if e.errno != errno.ENOENT:
189 189 raise
190 190 # checkisexec does not exist - fall through ...
191 191 else:
192 192 # checkisexec exists, check if it actually is exec
193 193 if m & EXECFLAGS != 0:
194 194 # ensure checkisexec exists, check it isn't exec
195 195 try:
196 196 m = os.stat(checknoexec).st_mode
197 197 except OSError as e:
198 198 if e.errno != errno.ENOENT:
199 199 raise
200 200 open(checknoexec, 'w').close() # might fail
201 201 m = os.stat(checknoexec).st_mode
202 202 if m & EXECFLAGS == 0:
203 203 # check-exec is exec and check-no-exec is not exec
204 204 return True
205 205 # checknoexec exists but is exec - delete it
206 206 unlink(checknoexec)
207 207 # checkisexec exists but is not exec - delete it
208 208 unlink(checkisexec)
209 209
210 210 # check using one file, leave it as checkisexec
211 211 checkdir = cachedir
212 212 else:
213 213 # check directly in path and don't leave checkisexec behind
214 214 checkdir = path
215 215 checkisexec = None
216 216 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-')
217 217 try:
218 218 os.close(fh)
219 219 m = os.stat(fn).st_mode
220 220 if m & EXECFLAGS == 0:
221 221 os.chmod(fn, m & 0o777 | EXECFLAGS)
222 222 if os.stat(fn).st_mode & EXECFLAGS != 0:
223 223 if checkisexec is not None:
224 224 os.rename(fn, checkisexec)
225 225 fn = None
226 226 return True
227 227 finally:
228 228 if fn is not None:
229 229 unlink(fn)
230 230 except (IOError, OSError):
231 231 # we don't care, the user probably won't be able to commit anyway
232 232 return False
233 233
234 234 def checklink(path):
235 235 """check whether the given path is on a symlink-capable filesystem"""
236 236 # mktemp is not racy because symlink creation will fail if the
237 237 # file already exists
238 238 while True:
239 239 cachedir = os.path.join(path, '.hg', 'cache')
240 240 checklink = os.path.join(cachedir, 'checklink')
241 241 # try fast path, read only
242 242 if os.path.islink(checklink):
243 243 return True
244 244 if os.path.isdir(cachedir):
245 245 checkdir = cachedir
246 246 else:
247 247 checkdir = path
248 248 cachedir = None
249 249 fscheckdir = pycompat.fsdecode(checkdir)
250 250 name = tempfile.mktemp(dir=fscheckdir,
251 251 prefix=r'checklink-')
252 252 name = pycompat.fsencode(name)
253 253 try:
254 254 fd = None
255 255 if cachedir is None:
256 256 fd = tempfile.NamedTemporaryFile(dir=fscheckdir,
257 257 prefix=r'hg-checklink-')
258 258 target = pycompat.fsencode(os.path.basename(fd.name))
259 259 else:
260 260 # create a fixed file to link to; doesn't matter if it
261 261 # already exists.
262 262 target = 'checklink-target'
263 263 try:
264 264 open(os.path.join(cachedir, target), 'w').close()
265 265 except IOError as inst:
266 266 if inst[0] == errno.EACCES:
267 267 # If we can't write to cachedir, just pretend
268 268 # that the fs is readonly and by association
269 269 # that the fs won't support symlinks. This
270 270 # seems like the least dangerous way to avoid
271 271 # data loss.
272 272 return False
273 273 raise
274 274 try:
275 275 os.symlink(target, name)
276 276 if cachedir is None:
277 277 unlink(name)
278 278 else:
279 279 try:
280 280 os.rename(name, checklink)
281 281 except OSError:
282 282 unlink(name)
283 283 return True
284 284 except OSError as inst:
285 285 # link creation might race, try again
286 286 if inst[0] == errno.EEXIST:
287 287 continue
288 288 raise
289 289 finally:
290 290 if fd is not None:
291 291 fd.close()
292 292 except AttributeError:
293 293 return False
294 294 except OSError as inst:
295 295 # sshfs might report failure while successfully creating the link
296 296 if inst[0] == errno.EIO and os.path.exists(name):
297 297 unlink(name)
298 298 return False
299 299
300 300 def checkosfilename(path):
301 301 '''Check that the base-relative path is a valid filename on this platform.
302 302 Returns None if the path is ok, or a UI string describing the problem.'''
303 pass # on posix platforms, every path is ok
303 return None # on posix platforms, every path is ok
304 304
305 305 def setbinary(fd):
306 306 pass
307 307
308 308 def pconvert(path):
309 309 return path
310 310
311 311 def localpath(path):
312 312 return path
313 313
314 314 def samefile(fpath1, fpath2):
315 315 """Returns whether path1 and path2 refer to the same file. This is only
316 316 guaranteed to work for files, not directories."""
317 317 return os.path.samefile(fpath1, fpath2)
318 318
319 319 def samedevice(fpath1, fpath2):
320 320 """Returns whether fpath1 and fpath2 are on the same device. This is only
321 321 guaranteed to work for files, not directories."""
322 322 st1 = os.lstat(fpath1)
323 323 st2 = os.lstat(fpath2)
324 324 return st1.st_dev == st2.st_dev
325 325
326 326 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
327 327 def normcase(path):
328 328 return path.lower()
329 329
330 330 # what normcase does to ASCII strings
331 331 normcasespec = encoding.normcasespecs.lower
332 332 # fallback normcase function for non-ASCII strings
333 333 normcasefallback = normcase
334 334
335 335 if pycompat.sysplatform == 'darwin':
336 336
337 337 def normcase(path):
338 338 '''
339 339 Normalize a filename for OS X-compatible comparison:
340 340 - escape-encode invalid characters
341 341 - decompose to NFD
342 342 - lowercase
343 343 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
344 344
345 345 >>> normcase(b'UPPER')
346 346 'upper'
347 347 >>> normcase(b'Caf\\xc3\\xa9')
348 348 'cafe\\xcc\\x81'
349 349 >>> normcase(b'\\xc3\\x89')
350 350 'e\\xcc\\x81'
351 351 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
352 352 '%b8%ca%c3\\xca\\xbe%c8.jpg'
353 353 '''
354 354
355 355 try:
356 356 return encoding.asciilower(path) # exception for non-ASCII
357 357 except UnicodeDecodeError:
358 358 return normcasefallback(path)
359 359
360 360 normcasespec = encoding.normcasespecs.lower
361 361
362 362 def normcasefallback(path):
363 363 try:
364 364 u = path.decode('utf-8')
365 365 except UnicodeDecodeError:
366 366 # OS X percent-encodes any bytes that aren't valid utf-8
367 367 s = ''
368 368 pos = 0
369 369 l = len(path)
370 370 while pos < l:
371 371 try:
372 372 c = encoding.getutf8char(path, pos)
373 373 pos += len(c)
374 374 except ValueError:
375 375 c = '%%%02X' % ord(path[pos:pos + 1])
376 376 pos += 1
377 377 s += c
378 378
379 379 u = s.decode('utf-8')
380 380
381 381 # Decompose then lowercase (HFS+ technote specifies lower)
382 382 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
383 383 # drop HFS+ ignored characters
384 384 return encoding.hfsignoreclean(enc)
385 385
386 386 if pycompat.sysplatform == 'cygwin':
387 387 # workaround for cygwin, in which mount point part of path is
388 388 # treated as case sensitive, even though underlying NTFS is case
389 389 # insensitive.
390 390
391 391 # default mount points
392 392 cygwinmountpoints = sorted([
393 393 "/usr/bin",
394 394 "/usr/lib",
395 395 "/cygdrive",
396 396 ], reverse=True)
397 397
398 398 # use upper-ing as normcase as same as NTFS workaround
399 399 def normcase(path):
400 400 pathlen = len(path)
401 401 if (pathlen == 0) or (path[0] != pycompat.ossep):
402 402 # treat as relative
403 403 return encoding.upper(path)
404 404
405 405 # to preserve case of mountpoint part
406 406 for mp in cygwinmountpoints:
407 407 if not path.startswith(mp):
408 408 continue
409 409
410 410 mplen = len(mp)
411 411 if mplen == pathlen: # mount point itself
412 412 return mp
413 413 if path[mplen] == pycompat.ossep:
414 414 return mp + encoding.upper(path[mplen:])
415 415
416 416 return encoding.upper(path)
417 417
418 418 normcasespec = encoding.normcasespecs.other
419 419 normcasefallback = normcase
420 420
421 421 # Cygwin translates native ACLs to POSIX permissions,
422 422 # but these translations are not supported by native
423 423 # tools, so the exec bit tends to be set erroneously.
424 424 # Therefore, disable executable bit access on Cygwin.
425 425 def checkexec(path):
426 426 return False
427 427
428 428 # Similarly, Cygwin's symlink emulation is likely to create
429 429 # problems when Mercurial is used from both Cygwin and native
430 430 # Windows, with other native tools, or on shared volumes
431 431 def checklink(path):
432 432 return False
433 433
434 434 _needsshellquote = None
435 435 def shellquote(s):
436 436 if pycompat.sysplatform == 'OpenVMS':
437 437 return '"%s"' % s
438 438 global _needsshellquote
439 439 if _needsshellquote is None:
440 440 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
441 441 if s and not _needsshellquote(s):
442 442 # "s" shouldn't have to be quoted
443 443 return s
444 444 else:
445 445 return "'%s'" % s.replace("'", "'\\''")
446 446
447 447 def quotecommand(cmd):
448 448 return cmd
449 449
450 450 def popen(command, mode='r'):
451 451 return os.popen(command, mode)
452 452
453 453 def testpid(pid):
454 454 '''return False if pid dead, True if running or not sure'''
455 455 if pycompat.sysplatform == 'OpenVMS':
456 456 return True
457 457 try:
458 458 os.kill(pid, 0)
459 459 return True
460 460 except OSError as inst:
461 461 return inst.errno != errno.ESRCH
462 462
463 463 def explainexit(code):
464 464 """return a 2-tuple (desc, code) describing a subprocess status
465 465 (codes from kill are negative - not os.system/wait encoding)"""
466 466 if code >= 0:
467 467 return _("exited with status %d") % code, code
468 468 return _("killed by signal %d") % -code, -code
469 469
470 470 def isowner(st):
471 471 """Return True if the stat object st is from the current user."""
472 472 return st.st_uid == os.getuid()
473 473
474 474 def findexe(command):
475 475 '''Find executable for command searching like which does.
476 476 If command is a basename then PATH is searched for command.
477 477 PATH isn't searched if command is an absolute or relative path.
478 478 If command isn't found None is returned.'''
479 479 if pycompat.sysplatform == 'OpenVMS':
480 480 return command
481 481
482 482 def findexisting(executable):
483 483 'Will return executable if existing file'
484 484 if os.path.isfile(executable) and os.access(executable, os.X_OK):
485 485 return executable
486 486 return None
487 487
488 488 if pycompat.ossep in command:
489 489 return findexisting(command)
490 490
491 491 if pycompat.sysplatform == 'plan9':
492 492 return findexisting(os.path.join('/bin', command))
493 493
494 494 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
495 495 executable = findexisting(os.path.join(path, command))
496 496 if executable is not None:
497 497 return executable
498 498 return None
499 499
500 500 def setsignalhandler():
501 501 pass
502 502
503 503 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
504 504
505 505 def statfiles(files):
506 506 '''Stat each file in files. Yield each stat, or None if a file does not
507 507 exist or has a type we don't care about.'''
508 508 lstat = os.lstat
509 509 getkind = stat.S_IFMT
510 510 for nf in files:
511 511 try:
512 512 st = lstat(nf)
513 513 if getkind(st.st_mode) not in _wantedkinds:
514 514 st = None
515 515 except OSError as err:
516 516 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
517 517 raise
518 518 st = None
519 519 yield st
520 520
521 521 def getuser():
522 522 '''return name of current user'''
523 523 return pycompat.fsencode(getpass.getuser())
524 524
525 525 def username(uid=None):
526 526 """Return the name of the user with the given uid.
527 527
528 528 If uid is None, return the name of the current user."""
529 529
530 530 if uid is None:
531 531 uid = os.getuid()
532 532 try:
533 533 return pwd.getpwuid(uid)[0]
534 534 except KeyError:
535 535 return str(uid)
536 536
537 537 def groupname(gid=None):
538 538 """Return the name of the group with the given gid.
539 539
540 540 If gid is None, return the name of the current group."""
541 541
542 542 if gid is None:
543 543 gid = os.getgid()
544 544 try:
545 545 return grp.getgrgid(gid)[0]
546 546 except KeyError:
547 547 return str(gid)
548 548
549 549 def groupmembers(name):
550 550 """Return the list of members of the group with the given
551 551 name, KeyError if the group does not exist.
552 552 """
553 553 return list(grp.getgrnam(name).gr_mem)
554 554
555 555 def spawndetached(args):
556 556 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
557 557 args[0], args)
558 558
559 559 def gethgcmd():
560 560 return sys.argv[:1]
561 561
562 562 def makedir(path, notindexed):
563 563 os.mkdir(path)
564 564
565 565 def lookupreg(key, name=None, scope=None):
566 566 return None
567 567
568 568 def hidewindow():
569 569 """Hide current shell window.
570 570
571 571 Used to hide the window opened when starting asynchronous
572 572 child process under Windows, unneeded on other systems.
573 573 """
574 574 pass
575 575
576 576 class cachestat(object):
577 577 def __init__(self, path):
578 578 self.stat = os.stat(path)
579 579
580 580 def cacheable(self):
581 581 return bool(self.stat.st_ino)
582 582
583 583 __hash__ = object.__hash__
584 584
585 585 def __eq__(self, other):
586 586 try:
587 587 # Only dev, ino, size, mtime and atime are likely to change. Out
588 588 # of these, we shouldn't compare atime but should compare the
589 589 # rest. However, one of the other fields changing indicates
590 590 # something fishy going on, so return False if anything but atime
591 591 # changes.
592 592 return (self.stat.st_mode == other.stat.st_mode and
593 593 self.stat.st_ino == other.stat.st_ino and
594 594 self.stat.st_dev == other.stat.st_dev and
595 595 self.stat.st_nlink == other.stat.st_nlink and
596 596 self.stat.st_uid == other.stat.st_uid and
597 597 self.stat.st_gid == other.stat.st_gid and
598 598 self.stat.st_size == other.stat.st_size and
599 599 self.stat.st_mtime == other.stat.st_mtime and
600 600 self.stat.st_ctime == other.stat.st_ctime)
601 601 except AttributeError:
602 602 return False
603 603
604 604 def __ne__(self, other):
605 605 return not self == other
606 606
607 607 def executablepath():
608 608 return None # available on Windows only
609 609
610 610 def statislink(st):
611 611 '''check whether a stat result is a symlink'''
612 612 return st and stat.S_ISLNK(st.st_mode)
613 613
614 614 def statisexec(st):
615 615 '''check whether a stat result is an executable file'''
616 616 return st and (st.st_mode & 0o100 != 0)
617 617
618 618 def poll(fds):
619 619 """block until something happens on any file descriptor
620 620
621 621 This is a generic helper that will check for any activity
622 622 (read, write. exception) and return the list of touched files.
623 623
624 624 In unsupported cases, it will raise a NotImplementedError"""
625 625 try:
626 626 while True:
627 627 try:
628 628 res = select.select(fds, fds, fds)
629 629 break
630 630 except select.error as inst:
631 631 if inst.args[0] == errno.EINTR:
632 632 continue
633 633 raise
634 634 except ValueError: # out of range file descriptor
635 635 raise NotImplementedError()
636 636 return sorted(list(set(sum(res, []))))
637 637
638 638 def readpipe(pipe):
639 639 """Read all available data from a pipe."""
640 640 # We can't fstat() a pipe because Linux will always report 0.
641 641 # So, we set the pipe to non-blocking mode and read everything
642 642 # that's available.
643 643 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
644 644 flags |= os.O_NONBLOCK
645 645 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
646 646
647 647 try:
648 648 chunks = []
649 649 while True:
650 650 try:
651 651 s = pipe.read()
652 652 if not s:
653 653 break
654 654 chunks.append(s)
655 655 except IOError:
656 656 break
657 657
658 658 return ''.join(chunks)
659 659 finally:
660 660 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
661 661
662 662 def bindunixsocket(sock, path):
663 663 """Bind the UNIX domain socket to the specified path"""
664 664 # use relative path instead of full path at bind() if possible, since
665 665 # AF_UNIX path has very small length limit (107 chars) on common
666 666 # platforms (see sys/un.h)
667 667 dirname, basename = os.path.split(path)
668 668 bakwdfd = None
669 669 if dirname:
670 670 bakwdfd = os.open('.', os.O_DIRECTORY)
671 671 os.chdir(dirname)
672 672 sock.bind(basename)
673 673 if bakwdfd:
674 674 os.fchdir(bakwdfd)
675 675 os.close(bakwdfd)
General Comments 0
You need to be logged in to leave comments. Login now