comparison mercurial/posix.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 97ada9b8d51b
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
38 # Some platforms build Python without os.link on systems that are 38 # Some platforms build Python without os.link on systems that are
39 # vaguely unix-like but don't have hardlink support. For those 39 # vaguely unix-like but don't have hardlink support. For those
40 # poor souls, just say we tried and that it failed so we fall back 40 # poor souls, just say we tried and that it failed so we fall back
41 # to copies. 41 # to copies.
42 def oslink(src, dst): 42 def oslink(src, dst):
43 raise OSError(errno.EINVAL, 43 raise OSError(
44 'hardlinks not supported: %s to %s' % (src, dst)) 44 errno.EINVAL, 'hardlinks not supported: %s to %s' % (src, dst)
45 )
46
47
45 readlink = os.readlink 48 readlink = os.readlink
46 unlink = os.unlink 49 unlink = os.unlink
47 rename = os.rename 50 rename = os.rename
48 removedirs = os.removedirs 51 removedirs = os.removedirs
49 expandglobs = False 52 expandglobs = False
50 53
51 umask = os.umask(0) 54 umask = os.umask(0)
52 os.umask(umask) 55 os.umask(umask)
53 56
54 if not pycompat.ispy3: 57 if not pycompat.ispy3:
58
55 def posixfile(name, mode=r'r', buffering=-1): 59 def posixfile(name, mode=r'r', buffering=-1):
56 fp = open(name, mode=mode, buffering=buffering) 60 fp = open(name, mode=mode, buffering=buffering)
57 # The position when opening in append mode is implementation defined, so 61 # The position when opening in append mode is implementation defined, so
58 # make it consistent by always seeking to the end. 62 # make it consistent by always seeking to the end.
59 if r'a' in mode: 63 if r'a' in mode:
60 fp.seek(0, os.SEEK_END) 64 fp.seek(0, os.SEEK_END)
61 return fp 65 return fp
66
67
62 else: 68 else:
63 # The underlying file object seeks as required in Python 3: 69 # The underlying file object seeks as required in Python 3:
64 # https://github.com/python/cpython/blob/v3.7.3/Modules/_io/fileio.c#L474 70 # https://github.com/python/cpython/blob/v3.7.3/Modules/_io/fileio.c#L474
65 posixfile = open 71 posixfile = open
72
66 73
67 def split(p): 74 def split(p):
68 '''Same as posixpath.split, but faster 75 '''Same as posixpath.split, but faster
69 76
70 >>> import posixpath 77 >>> import posixpath
84 nh = ht[0].rstrip('/') 91 nh = ht[0].rstrip('/')
85 if nh: 92 if nh:
86 return nh, ht[1] 93 return nh, ht[1]
87 return ht[0] + '/', ht[1] 94 return ht[0] + '/', ht[1]
88 95
96
89 def openhardlinks(): 97 def openhardlinks():
90 '''return true if it is safe to hold open file handles to hardlinks''' 98 '''return true if it is safe to hold open file handles to hardlinks'''
91 return True 99 return True
92 100
101
93 def nlinks(name): 102 def nlinks(name):
94 '''return number of hardlinks for the given file''' 103 '''return number of hardlinks for the given file'''
95 return os.lstat(name).st_nlink 104 return os.lstat(name).st_nlink
105
96 106
97 def parsepatchoutput(output_line): 107 def parsepatchoutput(output_line):
98 """parses the output produced by patch and returns the filename""" 108 """parses the output produced by patch and returns the filename"""
99 pf = output_line[14:] 109 pf = output_line[14:]
100 if pycompat.sysplatform == 'OpenVMS': 110 if pycompat.sysplatform == 'OpenVMS':
101 if pf[0] == '`': 111 if pf[0] == '`':
102 pf = pf[1:-1] # Remove the quotes 112 pf = pf[1:-1] # Remove the quotes
103 else: 113 else:
104 if pf.startswith("'") and pf.endswith("'") and " " in pf: 114 if pf.startswith("'") and pf.endswith("'") and " " in pf:
105 pf = pf[1:-1] # Remove the quotes 115 pf = pf[1:-1] # Remove the quotes
106 return pf 116 return pf
117
107 118
108 def sshargs(sshcmd, host, user, port): 119 def sshargs(sshcmd, host, user, port):
109 '''Build argument list for ssh''' 120 '''Build argument list for ssh'''
110 args = user and ("%s@%s" % (user, host)) or host 121 args = user and ("%s@%s" % (user, host)) or host
111 if '-' in args[:1]: 122 if '-' in args[:1]:
112 raise error.Abort( 123 raise error.Abort(
113 _('illegal ssh hostname or username starting with -: %s') % args) 124 _('illegal ssh hostname or username starting with -: %s') % args
125 )
114 args = shellquote(args) 126 args = shellquote(args)
115 if port: 127 if port:
116 args = '-p %s %s' % (shellquote(port), args) 128 args = '-p %s %s' % (shellquote(port), args)
117 return args 129 return args
118 130
131
119 def isexec(f): 132 def isexec(f):
120 """check whether a file is executable""" 133 """check whether a file is executable"""
121 return (os.lstat(f).st_mode & 0o100 != 0) 134 return os.lstat(f).st_mode & 0o100 != 0
135
122 136
123 def setflags(f, l, x): 137 def setflags(f, l, x):
124 st = os.lstat(f) 138 st = os.lstat(f)
125 s = st.st_mode 139 s = st.st_mode
126 if l: 140 if l:
144 data = os.readlink(f) 158 data = os.readlink(f)
145 unlink(f) 159 unlink(f)
146 fp = open(f, "wb") 160 fp = open(f, "wb")
147 fp.write(data) 161 fp.write(data)
148 fp.close() 162 fp.close()
149 s = 0o666 & ~umask # avoid restatting for chmod 163 s = 0o666 & ~umask # avoid restatting for chmod
150 164
151 sx = s & 0o100 165 sx = s & 0o100
152 if st.st_nlink > 1 and bool(x) != bool(sx): 166 if st.st_nlink > 1 and bool(x) != bool(sx):
153 # the file is a hardlink, break it 167 # the file is a hardlink, break it
154 with open(f, "rb") as fp: 168 with open(f, "rb") as fp:
162 # and obey umask. 176 # and obey umask.
163 os.chmod(f, s | (s & 0o444) >> 2 & ~umask) 177 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
164 elif not x and sx: 178 elif not x and sx:
165 # Turn off all +x bits 179 # Turn off all +x bits
166 os.chmod(f, s & 0o666) 180 os.chmod(f, s & 0o666)
181
167 182
168 def copymode(src, dst, mode=None, enforcewritable=False): 183 def copymode(src, dst, mode=None, enforcewritable=False):
169 '''Copy the file mode from the file at path src to dst. 184 '''Copy the file mode from the file at path src to dst.
170 If src doesn't exist, we're using mode instead. If mode is None, we're 185 If src doesn't exist, we're using mode instead. If mode is None, we're
171 using umask.''' 186 using umask.'''
183 198
184 if enforcewritable: 199 if enforcewritable:
185 new_mode |= stat.S_IWUSR 200 new_mode |= stat.S_IWUSR
186 201
187 os.chmod(dst, new_mode) 202 os.chmod(dst, new_mode)
203
188 204
189 def checkexec(path): 205 def checkexec(path):
190 """ 206 """
191 Check whether the given path is on a filesystem with UNIX-like exec flags 207 Check whether the given path is on a filesystem with UNIX-like exec flags
192 208
232 try: 248 try:
233 m = os.stat(checknoexec).st_mode 249 m = os.stat(checknoexec).st_mode
234 except OSError as e: 250 except OSError as e:
235 if e.errno != errno.ENOENT: 251 if e.errno != errno.ENOENT:
236 raise 252 raise
237 open(checknoexec, 'w').close() # might fail 253 open(checknoexec, 'w').close() # might fail
238 m = os.stat(checknoexec).st_mode 254 m = os.stat(checknoexec).st_mode
239 if m & EXECFLAGS == 0: 255 if m & EXECFLAGS == 0:
240 # check-exec is exec and check-no-exec is not exec 256 # check-exec is exec and check-no-exec is not exec
241 return True 257 return True
242 # checknoexec exists but is exec - delete it 258 # checknoexec exists but is exec - delete it
266 unlink(fn) 282 unlink(fn)
267 except (IOError, OSError): 283 except (IOError, OSError):
268 # we don't care, the user probably won't be able to commit anyway 284 # we don't care, the user probably won't be able to commit anyway
269 return False 285 return False
270 286
287
271 def checklink(path): 288 def checklink(path):
272 """check whether the given path is on a symlink-capable filesystem""" 289 """check whether the given path is on a symlink-capable filesystem"""
273 # mktemp is not racy because symlink creation will fail if the 290 # mktemp is not racy because symlink creation will fail if the
274 # file already exists 291 # file already exists
275 while True: 292 while True:
281 if os.path.isdir(cachedir): 298 if os.path.isdir(cachedir):
282 checkdir = cachedir 299 checkdir = cachedir
283 else: 300 else:
284 checkdir = path 301 checkdir = path
285 cachedir = None 302 cachedir = None
286 name = tempfile.mktemp(dir=pycompat.fsdecode(checkdir), 303 name = tempfile.mktemp(
287 prefix=r'checklink-') 304 dir=pycompat.fsdecode(checkdir), prefix=r'checklink-'
305 )
288 name = pycompat.fsencode(name) 306 name = pycompat.fsencode(name)
289 try: 307 try:
290 fd = None 308 fd = None
291 if cachedir is None: 309 if cachedir is None:
292 fd = pycompat.namedtempfile(dir=checkdir, 310 fd = pycompat.namedtempfile(
293 prefix='hg-checklink-') 311 dir=checkdir, prefix='hg-checklink-'
312 )
294 target = os.path.basename(fd.name) 313 target = os.path.basename(fd.name)
295 else: 314 else:
296 # create a fixed file to link to; doesn't matter if it 315 # create a fixed file to link to; doesn't matter if it
297 # already exists. 316 # already exists.
298 target = 'checklink-target' 317 target = 'checklink-target'
332 # sshfs might report failure while successfully creating the link 351 # sshfs might report failure while successfully creating the link
333 if inst.errno == errno.EIO and os.path.exists(name): 352 if inst.errno == errno.EIO and os.path.exists(name):
334 unlink(name) 353 unlink(name)
335 return False 354 return False
336 355
356
337 def checkosfilename(path): 357 def checkosfilename(path):
338 '''Check that the base-relative path is a valid filename on this platform. 358 '''Check that the base-relative path is a valid filename on this platform.
339 Returns None if the path is ok, or a UI string describing the problem.''' 359 Returns None if the path is ok, or a UI string describing the problem.'''
340 return None # on posix platforms, every path is ok 360 return None # on posix platforms, every path is ok
361
341 362
342 def getfsmountpoint(dirpath): 363 def getfsmountpoint(dirpath):
343 '''Get the filesystem mount point from a directory (best-effort) 364 '''Get the filesystem mount point from a directory (best-effort)
344 365
345 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. 366 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
346 ''' 367 '''
347 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath) 368 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
348 369
370
349 def getfstype(dirpath): 371 def getfstype(dirpath):
350 '''Get the filesystem type name from a directory (best-effort) 372 '''Get the filesystem type name from a directory (best-effort)
351 373
352 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. 374 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
353 ''' 375 '''
354 return getattr(osutil, 'getfstype', lambda x: None)(dirpath) 376 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
355 377
378
356 def setbinary(fd): 379 def setbinary(fd):
357 pass 380 pass
358 381
382
359 def pconvert(path): 383 def pconvert(path):
360 return path 384 return path
361 385
386
362 def localpath(path): 387 def localpath(path):
363 return path 388 return path
389
364 390
365 def samefile(fpath1, fpath2): 391 def samefile(fpath1, fpath2):
366 """Returns whether path1 and path2 refer to the same file. This is only 392 """Returns whether path1 and path2 refer to the same file. This is only
367 guaranteed to work for files, not directories.""" 393 guaranteed to work for files, not directories."""
368 return os.path.samefile(fpath1, fpath2) 394 return os.path.samefile(fpath1, fpath2)
395
369 396
370 def samedevice(fpath1, fpath2): 397 def samedevice(fpath1, fpath2):
371 """Returns whether fpath1 and fpath2 are on the same device. This is only 398 """Returns whether fpath1 and fpath2 are on the same device. This is only
372 guaranteed to work for files, not directories.""" 399 guaranteed to work for files, not directories."""
373 st1 = os.lstat(fpath1) 400 st1 = os.lstat(fpath1)
374 st2 = os.lstat(fpath2) 401 st2 = os.lstat(fpath2)
375 return st1.st_dev == st2.st_dev 402 return st1.st_dev == st2.st_dev
376 403
404
377 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems 405 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
378 def normcase(path): 406 def normcase(path):
379 return path.lower() 407 return path.lower()
408
380 409
381 # what normcase does to ASCII strings 410 # what normcase does to ASCII strings
382 normcasespec = encoding.normcasespecs.lower 411 normcasespec = encoding.normcasespecs.lower
383 # fallback normcase function for non-ASCII strings 412 # fallback normcase function for non-ASCII strings
384 normcasefallback = normcase 413 normcasefallback = normcase
421 while pos < l: 450 while pos < l:
422 try: 451 try:
423 c = encoding.getutf8char(path, pos) 452 c = encoding.getutf8char(path, pos)
424 pos += len(c) 453 pos += len(c)
425 except ValueError: 454 except ValueError:
426 c = '%%%02X' % ord(path[pos:pos + 1]) 455 c = '%%%02X' % ord(path[pos : pos + 1])
427 pos += 1 456 pos += 1
428 s += c 457 s += c
429 458
430 u = s.decode('utf-8') 459 u = s.decode('utf-8')
431 460
432 # Decompose then lowercase (HFS+ technote specifies lower) 461 # Decompose then lowercase (HFS+ technote specifies lower)
433 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8') 462 enc = unicodedata.normalize(r'NFD', u).lower().encode('utf-8')
434 # drop HFS+ ignored characters 463 # drop HFS+ ignored characters
435 return encoding.hfsignoreclean(enc) 464 return encoding.hfsignoreclean(enc)
436 465
466
437 if pycompat.sysplatform == 'cygwin': 467 if pycompat.sysplatform == 'cygwin':
438 # workaround for cygwin, in which mount point part of path is 468 # workaround for cygwin, in which mount point part of path is
439 # treated as case sensitive, even though underlying NTFS is case 469 # treated as case sensitive, even though underlying NTFS is case
440 # insensitive. 470 # insensitive.
441 471
442 # default mount points 472 # default mount points
443 cygwinmountpoints = sorted([ 473 cygwinmountpoints = sorted(
444 "/usr/bin", 474 ["/usr/bin", "/usr/lib", "/cygdrive",], reverse=True
445 "/usr/lib", 475 )
446 "/cygdrive",
447 ], reverse=True)
448 476
449 # use upper-ing as normcase as same as NTFS workaround 477 # use upper-ing as normcase as same as NTFS workaround
450 def normcase(path): 478 def normcase(path):
451 pathlen = len(path) 479 pathlen = len(path)
452 if (pathlen == 0) or (path[0] != pycompat.ossep): 480 if (pathlen == 0) or (path[0] != pycompat.ossep):
457 for mp in cygwinmountpoints: 485 for mp in cygwinmountpoints:
458 if not path.startswith(mp): 486 if not path.startswith(mp):
459 continue 487 continue
460 488
461 mplen = len(mp) 489 mplen = len(mp)
462 if mplen == pathlen: # mount point itself 490 if mplen == pathlen: # mount point itself
463 return mp 491 return mp
464 if path[mplen] == pycompat.ossep: 492 if path[mplen] == pycompat.ossep:
465 return mp + encoding.upper(path[mplen:]) 493 return mp + encoding.upper(path[mplen:])
466 494
467 return encoding.upper(path) 495 return encoding.upper(path)
480 # problems when Mercurial is used from both Cygwin and native 508 # problems when Mercurial is used from both Cygwin and native
481 # Windows, with other native tools, or on shared volumes 509 # Windows, with other native tools, or on shared volumes
482 def checklink(path): 510 def checklink(path):
483 return False 511 return False
484 512
513
485 _needsshellquote = None 514 _needsshellquote = None
515
516
486 def shellquote(s): 517 def shellquote(s):
487 if pycompat.sysplatform == 'OpenVMS': 518 if pycompat.sysplatform == 'OpenVMS':
488 return '"%s"' % s 519 return '"%s"' % s
489 global _needsshellquote 520 global _needsshellquote
490 if _needsshellquote is None: 521 if _needsshellquote is None:
493 # "s" shouldn't have to be quoted 524 # "s" shouldn't have to be quoted
494 return s 525 return s
495 else: 526 else:
496 return "'%s'" % s.replace("'", "'\\''") 527 return "'%s'" % s.replace("'", "'\\''")
497 528
529
498 def shellsplit(s): 530 def shellsplit(s):
499 """Parse a command string in POSIX shell way (best-effort)""" 531 """Parse a command string in POSIX shell way (best-effort)"""
500 return pycompat.shlexsplit(s, posix=True) 532 return pycompat.shlexsplit(s, posix=True)
501 533
534
502 def quotecommand(cmd): 535 def quotecommand(cmd):
503 return cmd 536 return cmd
537
504 538
505 def testpid(pid): 539 def testpid(pid):
506 '''return False if pid dead, True if running or not sure''' 540 '''return False if pid dead, True if running or not sure'''
507 if pycompat.sysplatform == 'OpenVMS': 541 if pycompat.sysplatform == 'OpenVMS':
508 return True 542 return True
510 os.kill(pid, 0) 544 os.kill(pid, 0)
511 return True 545 return True
512 except OSError as inst: 546 except OSError as inst:
513 return inst.errno != errno.ESRCH 547 return inst.errno != errno.ESRCH
514 548
549
515 def isowner(st): 550 def isowner(st):
516 """Return True if the stat object st is from the current user.""" 551 """Return True if the stat object st is from the current user."""
517 return st.st_uid == os.getuid() 552 return st.st_uid == os.getuid()
553
518 554
519 def findexe(command): 555 def findexe(command):
520 '''Find executable for command searching like which does. 556 '''Find executable for command searching like which does.
521 If command is a basename then PATH is searched for command. 557 If command is a basename then PATH is searched for command.
522 PATH isn't searched if command is an absolute or relative path. 558 PATH isn't searched if command is an absolute or relative path.
540 executable = findexisting(os.path.join(path, command)) 576 executable = findexisting(os.path.join(path, command))
541 if executable is not None: 577 if executable is not None:
542 return executable 578 return executable
543 return None 579 return None
544 580
581
545 def setsignalhandler(): 582 def setsignalhandler():
546 pass 583 pass
547 584
585
548 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK} 586 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
587
549 588
550 def statfiles(files): 589 def statfiles(files):
551 '''Stat each file in files. Yield each stat, or None if a file does not 590 '''Stat each file in files. Yield each stat, or None if a file does not
552 exist or has a type we don't care about.''' 591 exist or has a type we don't care about.'''
553 lstat = os.lstat 592 lstat = os.lstat
561 if err.errno not in (errno.ENOENT, errno.ENOTDIR): 600 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
562 raise 601 raise
563 st = None 602 st = None
564 yield st 603 yield st
565 604
605
566 def getuser(): 606 def getuser():
567 '''return name of current user''' 607 '''return name of current user'''
568 return pycompat.fsencode(getpass.getuser()) 608 return pycompat.fsencode(getpass.getuser())
609
569 610
570 def username(uid=None): 611 def username(uid=None):
571 """Return the name of the user with the given uid. 612 """Return the name of the user with the given uid.
572 613
573 If uid is None, return the name of the current user.""" 614 If uid is None, return the name of the current user."""
577 try: 618 try:
578 return pycompat.fsencode(pwd.getpwuid(uid)[0]) 619 return pycompat.fsencode(pwd.getpwuid(uid)[0])
579 except KeyError: 620 except KeyError:
580 return b'%d' % uid 621 return b'%d' % uid
581 622
623
582 def groupname(gid=None): 624 def groupname(gid=None):
583 """Return the name of the group with the given gid. 625 """Return the name of the group with the given gid.
584 626
585 If gid is None, return the name of the current group.""" 627 If gid is None, return the name of the current group."""
586 628
589 try: 631 try:
590 return pycompat.fsencode(grp.getgrgid(gid)[0]) 632 return pycompat.fsencode(grp.getgrgid(gid)[0])
591 except KeyError: 633 except KeyError:
592 return pycompat.bytestr(gid) 634 return pycompat.bytestr(gid)
593 635
636
594 def groupmembers(name): 637 def groupmembers(name):
595 """Return the list of members of the group with the given 638 """Return the list of members of the group with the given
596 name, KeyError if the group does not exist. 639 name, KeyError if the group does not exist.
597 """ 640 """
598 name = pycompat.fsdecode(name) 641 name = pycompat.fsdecode(name)
599 return pycompat.rapply(pycompat.fsencode, list(grp.getgrnam(name).gr_mem)) 642 return pycompat.rapply(pycompat.fsencode, list(grp.getgrnam(name).gr_mem))
600 643
644
601 def spawndetached(args): 645 def spawndetached(args):
602 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), 646 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), args[0], args)
603 args[0], args) 647
604 648
605 def gethgcmd(): 649 def gethgcmd():
606 return sys.argv[:1] 650 return sys.argv[:1]
607 651
652
608 def makedir(path, notindexed): 653 def makedir(path, notindexed):
609 os.mkdir(path) 654 os.mkdir(path)
610 655
656
611 def lookupreg(key, name=None, scope=None): 657 def lookupreg(key, name=None, scope=None):
612 return None 658 return None
659
613 660
614 def hidewindow(): 661 def hidewindow():
615 """Hide current shell window. 662 """Hide current shell window.
616 663
617 Used to hide the window opened when starting asynchronous 664 Used to hide the window opened when starting asynchronous
618 child process under Windows, unneeded on other systems. 665 child process under Windows, unneeded on other systems.
619 """ 666 """
620 pass 667 pass
668
621 669
622 class cachestat(object): 670 class cachestat(object):
623 def __init__(self, path): 671 def __init__(self, path):
624 self.stat = os.stat(path) 672 self.stat = os.stat(path)
625 673
633 # Only dev, ino, size, mtime and atime are likely to change. Out 681 # Only dev, ino, size, mtime and atime are likely to change. Out
634 # of these, we shouldn't compare atime but should compare the 682 # of these, we shouldn't compare atime but should compare the
635 # rest. However, one of the other fields changing indicates 683 # rest. However, one of the other fields changing indicates
636 # something fishy going on, so return False if anything but atime 684 # something fishy going on, so return False if anything but atime
637 # changes. 685 # changes.
638 return (self.stat.st_mode == other.stat.st_mode and 686 return (
639 self.stat.st_ino == other.stat.st_ino and 687 self.stat.st_mode == other.stat.st_mode
640 self.stat.st_dev == other.stat.st_dev and 688 and self.stat.st_ino == other.stat.st_ino
641 self.stat.st_nlink == other.stat.st_nlink and 689 and self.stat.st_dev == other.stat.st_dev
642 self.stat.st_uid == other.stat.st_uid and 690 and self.stat.st_nlink == other.stat.st_nlink
643 self.stat.st_gid == other.stat.st_gid and 691 and self.stat.st_uid == other.stat.st_uid
644 self.stat.st_size == other.stat.st_size and 692 and self.stat.st_gid == other.stat.st_gid
645 self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME] and 693 and self.stat.st_size == other.stat.st_size
646 self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME]) 694 and self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME]
695 and self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME]
696 )
647 except AttributeError: 697 except AttributeError:
648 return False 698 return False
649 699
650 def __ne__(self, other): 700 def __ne__(self, other):
651 return not self == other 701 return not self == other
702
652 703
653 def statislink(st): 704 def statislink(st):
654 '''check whether a stat result is a symlink''' 705 '''check whether a stat result is a symlink'''
655 return st and stat.S_ISLNK(st.st_mode) 706 return st and stat.S_ISLNK(st.st_mode)
656 707
708
657 def statisexec(st): 709 def statisexec(st):
658 '''check whether a stat result is an executable file''' 710 '''check whether a stat result is an executable file'''
659 return st and (st.st_mode & 0o100 != 0) 711 return st and (st.st_mode & 0o100 != 0)
712
660 713
661 def poll(fds): 714 def poll(fds):
662 """block until something happens on any file descriptor 715 """block until something happens on any file descriptor
663 716
664 This is a generic helper that will check for any activity 717 This is a generic helper that will check for any activity
672 break 725 break
673 except select.error as inst: 726 except select.error as inst:
674 if inst.args[0] == errno.EINTR: 727 if inst.args[0] == errno.EINTR:
675 continue 728 continue
676 raise 729 raise
677 except ValueError: # out of range file descriptor 730 except ValueError: # out of range file descriptor
678 raise NotImplementedError() 731 raise NotImplementedError()
679 return sorted(list(set(sum(res, [])))) 732 return sorted(list(set(sum(res, []))))
733
680 734
681 def readpipe(pipe): 735 def readpipe(pipe):
682 """Read all available data from a pipe.""" 736 """Read all available data from a pipe."""
683 # We can't fstat() a pipe because Linux will always report 0. 737 # We can't fstat() a pipe because Linux will always report 0.
684 # So, we set the pipe to non-blocking mode and read everything 738 # So, we set the pipe to non-blocking mode and read everything
699 break 753 break
700 754
701 return ''.join(chunks) 755 return ''.join(chunks)
702 finally: 756 finally:
703 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags) 757 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
758
704 759
705 def bindunixsocket(sock, path): 760 def bindunixsocket(sock, path):
706 """Bind the UNIX domain socket to the specified path""" 761 """Bind the UNIX domain socket to the specified path"""
707 # use relative path instead of full path at bind() if possible, since 762 # use relative path instead of full path at bind() if possible, since
708 # AF_UNIX path has very small length limit (107 chars) on common 763 # AF_UNIX path has very small length limit (107 chars) on common