mercurial/util.py
changeset 36524 bfe38f787d5b
parent 36432 1ca4e86c7265
child 36525 3158052720ae
equal deleted inserted replaced
36523:e7411fb7ba7f 36524:bfe38f787d5b
   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__