485 stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
485 stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
486 stderr=subprocess.PIPE, |
486 stderr=subprocess.PIPE, |
487 universal_newlines=newlines, |
487 universal_newlines=newlines, |
488 env=env) |
488 env=env) |
489 return p.stdin, p.stdout, p.stderr, p |
489 return p.stdin, p.stdout, p.stderr, p |
|
490 |
|
491 class fileobjectproxy(object): |
|
492 """A proxy around file objects that tells a watcher when events occur. |
|
493 |
|
494 This type is intended to only be used for testing purposes. Think hard |
|
495 before using it in important code. |
|
496 """ |
|
497 __slots__ = ( |
|
498 r'_orig', |
|
499 r'_observer', |
|
500 ) |
|
501 |
|
502 def __init__(self, fh, observer): |
|
503 object.__setattr__(self, '_orig', fh) |
|
504 object.__setattr__(self, '_observer', observer) |
|
505 |
|
506 def __getattribute__(self, name): |
|
507 ours = { |
|
508 # IOBase |
|
509 r'close', |
|
510 # closed if a property |
|
511 r'fileno', |
|
512 r'flush', |
|
513 r'isatty', |
|
514 r'readable', |
|
515 r'readline', |
|
516 r'readlines', |
|
517 r'seek', |
|
518 r'seekable', |
|
519 r'tell', |
|
520 r'truncate', |
|
521 r'writable', |
|
522 r'writelines', |
|
523 # RawIOBase |
|
524 r'read', |
|
525 r'readall', |
|
526 r'readinto', |
|
527 r'write', |
|
528 # BufferedIOBase |
|
529 # raw is a property |
|
530 r'detach', |
|
531 # read defined above |
|
532 r'read1', |
|
533 # readinto defined above |
|
534 # write defined above |
|
535 } |
|
536 |
|
537 # We only observe some methods. |
|
538 if name in ours: |
|
539 return object.__getattribute__(self, name) |
|
540 |
|
541 return getattr(object.__getattribute__(self, r'_orig'), name) |
|
542 |
|
543 def __delattr__(self, name): |
|
544 return delattr(object.__getattribute__(self, r'_orig'), name) |
|
545 |
|
546 def __setattr__(self, name, value): |
|
547 return setattr(object.__getattribute__(self, r'_orig'), name, value) |
|
548 |
|
549 def __iter__(self): |
|
550 return object.__getattribute__(self, r'_orig').__iter__() |
|
551 |
|
552 def _observedcall(self, name, *args, **kwargs): |
|
553 # Call the original object. |
|
554 orig = object.__getattribute__(self, r'_orig') |
|
555 res = getattr(orig, name)(*args, **kwargs) |
|
556 |
|
557 # Call a method on the observer of the same name with arguments |
|
558 # so it can react, log, etc. |
|
559 observer = object.__getattribute__(self, r'_observer') |
|
560 fn = getattr(observer, name, None) |
|
561 if fn: |
|
562 fn(res, *args, **kwargs) |
|
563 |
|
564 return res |
|
565 |
|
566 def close(self, *args, **kwargs): |
|
567 return object.__getattribute__(self, r'_observedcall')( |
|
568 r'close', *args, **kwargs) |
|
569 |
|
570 def fileno(self, *args, **kwargs): |
|
571 return object.__getattribute__(self, r'_observedcall')( |
|
572 r'fileno', *args, **kwargs) |
|
573 |
|
574 def flush(self, *args, **kwargs): |
|
575 return object.__getattribute__(self, r'_observedcall')( |
|
576 r'flush', *args, **kwargs) |
|
577 |
|
578 def isatty(self, *args, **kwargs): |
|
579 return object.__getattribute__(self, r'_observedcall')( |
|
580 r'isatty', *args, **kwargs) |
|
581 |
|
582 def readable(self, *args, **kwargs): |
|
583 return object.__getattribute__(self, r'_observedcall')( |
|
584 r'readable', *args, **kwargs) |
|
585 |
|
586 def readline(self, *args, **kwargs): |
|
587 return object.__getattribute__(self, r'_observedcall')( |
|
588 r'readline', *args, **kwargs) |
|
589 |
|
590 def readlines(self, *args, **kwargs): |
|
591 return object.__getattribute__(self, r'_observedcall')( |
|
592 r'readlines', *args, **kwargs) |
|
593 |
|
594 def seek(self, *args, **kwargs): |
|
595 return object.__getattribute__(self, r'_observedcall')( |
|
596 r'seek', *args, **kwargs) |
|
597 |
|
598 def seekable(self, *args, **kwargs): |
|
599 return object.__getattribute__(self, r'_observedcall')( |
|
600 r'seekable', *args, **kwargs) |
|
601 |
|
602 def tell(self, *args, **kwargs): |
|
603 return object.__getattribute__(self, r'_observedcall')( |
|
604 r'tell', *args, **kwargs) |
|
605 |
|
606 def truncate(self, *args, **kwargs): |
|
607 return object.__getattribute__(self, r'_observedcall')( |
|
608 r'truncate', *args, **kwargs) |
|
609 |
|
610 def writable(self, *args, **kwargs): |
|
611 return object.__getattribute__(self, r'_observedcall')( |
|
612 r'writable', *args, **kwargs) |
|
613 |
|
614 def writelines(self, *args, **kwargs): |
|
615 return object.__getattribute__(self, r'_observedcall')( |
|
616 r'writelines', *args, **kwargs) |
|
617 |
|
618 def read(self, *args, **kwargs): |
|
619 return object.__getattribute__(self, r'_observedcall')( |
|
620 r'read', *args, **kwargs) |
|
621 |
|
622 def readall(self, *args, **kwargs): |
|
623 return object.__getattribute__(self, r'_observedcall')( |
|
624 r'readall', *args, **kwargs) |
|
625 |
|
626 def readinto(self, *args, **kwargs): |
|
627 return object.__getattribute__(self, r'_observedcall')( |
|
628 r'readinto', *args, **kwargs) |
|
629 |
|
630 def write(self, *args, **kwargs): |
|
631 return object.__getattribute__(self, r'_observedcall')( |
|
632 r'write', *args, **kwargs) |
|
633 |
|
634 def detach(self, *args, **kwargs): |
|
635 return object.__getattribute__(self, r'_observedcall')( |
|
636 r'detach', *args, **kwargs) |
|
637 |
|
638 def read1(self, *args, **kwargs): |
|
639 return object.__getattribute__(self, r'_observedcall')( |
|
640 r'read1', *args, **kwargs) |
|
641 |
|
642 DATA_ESCAPE_MAP = {pycompat.bytechr(i): br'\x%02x' % i for i in range(256)} |
|
643 DATA_ESCAPE_MAP.update({ |
|
644 b'\\': b'\\\\', |
|
645 b'\r': br'\r', |
|
646 b'\n': br'\n', |
|
647 }) |
|
648 DATA_ESCAPE_RE = remod.compile(br'[\x00-\x08\x0a-\x1f\\\x7f-\xff]') |
|
649 |
|
650 def escapedata(s): |
|
651 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s) |
|
652 |
|
653 class fileobjectobserver(object): |
|
654 """Logs file object activity.""" |
|
655 def __init__(self, fh, name, reads=True, writes=True, logdata=False): |
|
656 self.fh = fh |
|
657 self.name = name |
|
658 self.logdata = logdata |
|
659 self.reads = reads |
|
660 self.writes = writes |
|
661 |
|
662 def _writedata(self, data): |
|
663 if not self.logdata: |
|
664 self.fh.write('\n') |
|
665 return |
|
666 |
|
667 # Simple case writes all data on a single line. |
|
668 if b'\n' not in data: |
|
669 self.fh.write(': %s\n' % escapedata(data)) |
|
670 return |
|
671 |
|
672 # Data with newlines is written to multiple lines. |
|
673 self.fh.write(':\n') |
|
674 lines = data.splitlines(True) |
|
675 for line in lines: |
|
676 self.fh.write('%s> %s\n' % (self.name, escapedata(line))) |
|
677 |
|
678 def read(self, res, size=-1): |
|
679 if not self.reads: |
|
680 return |
|
681 |
|
682 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res))) |
|
683 self._writedata(res) |
|
684 |
|
685 def readline(self, res, limit=-1): |
|
686 if not self.reads: |
|
687 return |
|
688 |
|
689 self.fh.write('%s> readline() -> %d' % (self.name, len(res))) |
|
690 self._writedata(res) |
|
691 |
|
692 def write(self, res, data): |
|
693 if not self.writes: |
|
694 return |
|
695 |
|
696 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res)) |
|
697 self._writedata(data) |
|
698 |
|
699 def flush(self, res): |
|
700 if not self.writes: |
|
701 return |
|
702 |
|
703 self.fh.write('%s> flush() -> %r\n' % (self.name, res)) |
|
704 |
|
705 def makeloggingfileobject(logh, fh, name, reads=True, writes=True, |
|
706 logdata=False): |
|
707 """Turn a file object into a logging file object.""" |
|
708 |
|
709 observer = fileobjectobserver(logh, name, reads=reads, writes=writes, |
|
710 logdata=logdata) |
|
711 return fileobjectproxy(fh, observer) |
490 |
712 |
491 def version(): |
713 def version(): |
492 """Return version information if available.""" |
714 """Return version information if available.""" |
493 try: |
715 try: |
494 from . import __version__ |
716 from . import __version__ |