Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Unified Diff: gclient_utils.py

Issue 8370004: Improve the sys.stdout proxy to be more transparent. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/gclient_utils_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gclient_utils.py
diff --git a/gclient_utils.py b/gclient_utils.py
index e65893468469fed10c25c2c1f031d2075f8a7123..1981f36e70e07b26eb43052368d298b548915ea8 100644
--- a/gclient_utils.py
+++ b/gclient_utils.py
@@ -217,124 +217,120 @@ def CheckCallAndFilterAndHeader(args, always=False, **kwargs):
return CheckCallAndFilter(args, **kwargs)
-def SoftClone(obj):
- """Clones an object. copy.copy() doesn't work on 'file' objects."""
- if obj.__class__.__name__ == 'SoftCloned':
- return obj
- class SoftCloned(object):
- pass
- new_obj = SoftCloned()
- for member in dir(obj):
- if member.startswith('_'):
- continue
- setattr(new_obj, member, getattr(obj, member))
- return new_obj
+class Wrapper(object):
+ """Wraps an object, acting as a transparent proxy for all properties by
+ default.
+ """
+ def __init__(self, wrapped):
+ self._wrapped = wrapped
+ def __getattr__(self, name):
+ return getattr(self._wrapped, name)
-def MakeFileAutoFlush(fileobj, delay=10):
+
+class AutoFlush(Wrapper):
"""Creates a file object clone to automatically flush after N seconds."""
- if hasattr(fileobj, 'last_flushed_at'):
- # Already patched. Just update delay.
- fileobj.delay = delay
- return fileobj
+ def __init__(self, wrapped, delay):
+ super(AutoFlush, self).__init__(wrapped)
+ if not hasattr(self, 'lock'):
+ self.lock = threading.Lock()
+ self.__last_flushed_at = time.time()
+ self.delay = delay
+
+ @property
+ def autoflush(self):
+ return self
- # Attribute 'XXX' defined outside __init__
- # pylint: disable=W0201
- new_fileobj = SoftClone(fileobj)
- if not hasattr(new_fileobj, 'lock'):
- new_fileobj.lock = threading.Lock()
- new_fileobj.last_flushed_at = time.time()
- new_fileobj.delay = delay
- new_fileobj.old_auto_flush_write = new_fileobj.write
- # Silence pylint.
- new_fileobj.flush = fileobj.flush
-
- def auto_flush_write(out):
- new_fileobj.old_auto_flush_write(out)
+ def write(self, out, *args, **kwargs):
+ self._wrapped.write(out, *args, **kwargs)
should_flush = False
- new_fileobj.lock.acquire()
+ self.lock.acquire()
try:
- if (new_fileobj.delay and
- (time.time() - new_fileobj.last_flushed_at) > new_fileobj.delay):
+ if self.delay and (time.time() - self.__last_flushed_at) > self.delay:
should_flush = True
- new_fileobj.last_flushed_at = time.time()
+ self.__last_flushed_at = time.time()
finally:
Dirk Pranke 2011/10/21 21:14:48 Nit: what exceptions could be raised in here that
M-A Ruel 2011/10/22 01:36:00 Someone set sys.stdout.delay = None by error. If t
- new_fileobj.lock.release()
+ self.lock.release()
if should_flush:
- new_fileobj.flush()
-
- new_fileobj.write = auto_flush_write
- return new_fileobj
+ self.flush()
-def MakeFileAnnotated(fileobj, include_zero=False):
+class Annotated(Wrapper):
"""Creates a file object clone to automatically prepends every line in worker
- threads with a NN> prefix."""
- if hasattr(fileobj, 'output_buffers'):
- # Already patched.
- return fileobj
+ threads with a NN> prefix.
+ """
+ def __init__(self, wrapped, include_zero=False):
+ super(Annotated, self).__init__(wrapped)
+ if not hasattr(self, 'lock'):
+ self.lock = threading.Lock()
+ self.__output_buffers = {}
+ self.__include_zero = include_zero
- # Attribute 'XXX' defined outside __init__
- # pylint: disable=W0201
- new_fileobj = SoftClone(fileobj)
- if not hasattr(new_fileobj, 'lock'):
- new_fileobj.lock = threading.Lock()
- new_fileobj.output_buffers = {}
- new_fileobj.old_annotated_write = new_fileobj.write
-
- def annotated_write(out):
- index = getattr(threading.currentThread(), 'index', None)
- if index is None:
- if not include_zero:
- # Unindexed threads aren't buffered.
- new_fileobj.old_annotated_write(out)
- return
- index = 0
-
- new_fileobj.lock.acquire()
+ @property
+ def annotated(self):
+ return self
+
+ def write(self, out):
+ index = getattr(threading.currentThread(), 'index', 0)
+ if not index and not self.__include_zero:
+ # Unindexed threads aren't buffered.
+ return self._wrapped.write(out)
+
+ self.lock.acquire()
try:
# Use a dummy array to hold the string so the code can be lockless.
# Strings are immutable, requiring to keep a lock for the whole dictionary
# otherwise. Using an array is faster than using a dummy object.
- if not index in new_fileobj.output_buffers:
- obj = new_fileobj.output_buffers[index] = ['']
+ if not index in self.__output_buffers:
+ obj = self.__output_buffers[index] = ['']
else:
- obj = new_fileobj.output_buffers[index]
+ obj = self.__output_buffers[index]
finally:
- new_fileobj.lock.release()
+ self.lock.release()
# Continue lockless.
obj[0] += out
while '\n' in obj[0]:
line, remaining = obj[0].split('\n', 1)
if line:
- new_fileobj.old_annotated_write('%d>%s\n' % (index, line))
+ self._wrapped.write('%d>%s\n' % (index, line))
obj[0] = remaining
- def full_flush():
+ def flush(self):
"""Flush buffered output."""
orphans = []
- new_fileobj.lock.acquire()
+ self.lock.acquire()
try:
# Detect threads no longer existing.
indexes = (getattr(t, 'index', None) for t in threading.enumerate())
indexes = filter(None, indexes)
- for index in new_fileobj.output_buffers:
+ for index in self.__output_buffers:
if not index in indexes:
- orphans.append((index, new_fileobj.output_buffers[index][0]))
+ orphans.append((index, self.__output_buffers[index][0]))
for orphan in orphans:
- del new_fileobj.output_buffers[orphan[0]]
+ del self.__output_buffers[orphan[0]]
finally:
- new_fileobj.lock.release()
+ self.lock.release()
# Don't keep the lock while writting. Will append \n when it shouldn't.
for orphan in orphans:
if orphan[1]:
- new_fileobj.old_annotated_write('%d>%s\n' % (orphan[0], orphan[1]))
+ self._wrapped.write('%d>%s\n' % (orphan[0], orphan[1]))
+ return self._wrapped.flush()
+
- new_fileobj.write = annotated_write
- new_fileobj.full_flush = full_flush
- return new_fileobj
+def MakeFileAutoFlush(fileobj, delay=10):
+ autoflush = getattr(fileobj, 'autoflush', None)
+ if autoflush:
+ autoflush.delay = delay
+ return fileobj
+ return AutoFlush(fileobj, delay)
+
+
+def MakeFileAnnotated(fileobj, include_zero=False):
+ if getattr(fileobj, 'annotated', None):
+ return fileobj
+ return Annotated(fileobj)
def CheckCallAndFilter(args, stdout=None, filter_fn=None,
@@ -638,7 +634,7 @@ class ExecutionQueue(object):
self.running.append(t)
else:
t.join()
- sys.stdout.full_flush() # pylint: disable=E1101
+ sys.stdout.flush()
if self.progress:
self.progress.update(1, t.item.name)
if t.item.name in self.ran:
« no previous file with comments | « no previous file | tests/gclient_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698