Chromium Code Reviews| Index: build/android/pylib/base/output_manager.py |
| diff --git a/build/android/pylib/base/output_manager.py b/build/android/pylib/base/output_manager.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..952aac8c708281a1e4c92dd3ed67b3d1eb2ecfae |
| --- /dev/null |
| +++ b/build/android/pylib/base/output_manager.py |
| @@ -0,0 +1,144 @@ |
| +# Copyright 2017 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import contextlib |
| +import logging |
| +import os |
| +import tempfile |
| + |
| +from devil.utils import reraiser_thread |
| + |
| + |
| +class Datatype(object): |
| + HTML = 'html' |
| + IMAGE = 'image' |
| + TEXT = 'text' |
| + |
| + |
| +class OutputManager(object): |
| + |
| + def __init__(self): |
| + """OutputManager Constructor. |
| + |
| + This class provides a simple interface to save test output. Subclasses |
| + of this will allow users to save test results in the cloud or locally. |
| + """ |
| + self._allow_upload = False |
| + self._thread_group = None |
| + |
| + @contextlib.contextmanager |
| + def ArchivedTempfile( |
|
mikecase (-- gone --)
2017/08/23 04:28:20
This is the main function to look at.
|
| + self, out_filename, out_subdir, datatype=Datatype.TEXT): |
| + """Archive file contents asynchonously and then deletes file. |
| + |
| + Args: |
| + out_filename: Name for saved file. |
| + out_subdir: Directory to save |out_file| in. |
| + datatype: Datatype of file. |
| + |
| + Returns: |
| + A ArchivedFile file. This file will be uploaded async when the context |
| + manager exits. AFTER the context manager exits, you can get the link to |
| + where the file will be stored using the Link() API. You can use typical |
| + file APIs to write and flish the ArchivedFile. You can also use file.name |
| + to get the local filepath to where the underlying file exists. If you do |
| + this, you are responsible of flushing the file before exiting the context |
| + manager. |
| + """ |
| + if not self._allow_upload: |
| + raise Exception('Must run |SetUp| before attempting to upload!') |
| + |
| + f = self._CreateArchivedFile(out_filename, out_subdir, datatype) |
| + yield f |
| + f.PrepareArchive() |
| + |
| + def archive(): |
| + try: |
| + f.Archive() |
| + finally: |
| + os.remove(f.name) |
| + |
| + thread = reraiser_thread.ReraiserThread(func=archive) |
| + thread.start() |
| + self._thread_group.Add(thread) |
| + |
| + def _CreateArchivedFile(self, out_filename, out_subdir, datatype): |
| + """Returns an instance of Job that actually uploads/saves the file.""" |
| + raise NotImplementedError |
| + |
| + def SetUp(self): |
| + self._allow_upload = True |
| + self._thread_group = reraiser_thread.ReraiserThreadGroup() |
| + |
| + def TearDown(self): |
| + self._allow_upload = False |
| + logging.info('Finishing archiving output.') |
| + self._thread_group.JoinAll() |
| + |
| + def __enter__(self): |
| + self.SetUp() |
| + return self |
| + |
| + def __exit__(self, _exc_type, _exc_val, _exc_tb): |
| + self.TearDown() |
| + |
| +class ArchivedFile(object): |
| + |
| + def __init__(self, out_filename, out_subdir, datatype): |
| + self._out_filename = out_filename |
| + self._out_subdir = out_subdir |
| + self._datatype = datatype |
| + |
| + self._f = tempfile.NamedTemporaryFile(delete=False) |
| + self.name = self._f.name |
| + self._ready_to_archive = False |
| + |
| + def write(self, *args, **kwargs): |
| + if self._ready_to_archive: |
| + raise Exception('Cannot write to file after archiving has begun!') |
| + self._f.write(*args, **kwargs) |
| + |
| + def flush(self, *args, **kwargs): |
| + if self._ready_to_archive: |
| + raise Exception('Cannot flush file after archiving has begun!') |
| + self._f.flush(*args, **kwargs) |
| + |
| + def Link(self): |
| + """Returns location of archived file.""" |
| + if not self._ready_to_archive: |
| + raise Exception('Cannot get link to archived file before archiving' |
| + 'has begun') |
| + return self._Link() |
| + |
| + def _Link(self): |
| + """Note for when overriding this function. |
| + |
| + This function will certainly be called before the file |
| + has finished being archived. Therefore, this needs to be able to know the |
| + exact location of the archived file before it is finished being archived. |
| + """ |
| + raise NotImplementedError |
| + |
| + def PrepareArchive(self): |
| + """Meant to be called synchronously to prepare file for async archiving.""" |
| + self.flush() |
| + self._ready_to_archive = True |
| + self._PrepareArchive() |
| + |
| + def _PrepareArchive(self): |
| + """Note for when overriding this function. |
| + |
| + This function is needed for things such as computing the location of |
| + content addressed files. This is called after the file is written but |
| + before archiving has begun. |
| + """ |
| + pass |
| + |
| + def Archive(self): |
| + """Archives file.""" |
| + assert self._ready_to_archive |
| + self._Archive() |
| + |
| + def _Archive(self): |
| + raise NotImplementedError |