Show More
@@ -488,6 +488,228 b' def popen4(cmd, env=None, newlines=False' | |||
|
488 | 488 | env=env) |
|
489 | 489 | return p.stdin, p.stdout, p.stderr, p |
|
490 | 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) | |
|
712 | ||
|
491 | 713 | def version(): |
|
492 | 714 | """Return version information if available.""" |
|
493 | 715 | try: |
General Comments 0
You need to be logged in to leave comments.
Login now