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