def get_console_output(self, build_id, subjob_id, atom_id, result_root, max_lines=50, offset_line=None): """ Return the console output if it exists, raises an ItemNotFound error if not. On success, the response contains keys: offset_line, num_lines, total_num_lines, and content. e.g.: { 'offset_line': 0, 'num_lines': 50, 'total_num_lines': 167, 'content': 'Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\n...', } :type build_id: int :type subjob_id: int :type atom_id: int :param result_root: the sys path to either the results or artifacts directory where results are stored. :type result_root: str :param max_lines: The maximum total number of lines to return. If this max_lines + offset_line lines do not exist in the output file, just return what there is. :type max_lines: int :param offset_line: The line number (0-indexed) to start reading content for. If none is specified, we will return the console output starting from the end of the file. :type offset_line: int | None """ if offset_line is not None and offset_line < 0: raise BadRequestError('\'offset_line\' must be greater than or equal to zero.') if max_lines <= 0: raise BadRequestError('\'max_lines\' must be greater than zero.') artifact_dir = BuildArtifact.atom_artifact_directory(build_id, subjob_id, atom_id, result_root=result_root) output_file = os.path.join(artifact_dir, BuildArtifact.OUTPUT_FILE) if not os.path.isfile(output_file): raise ItemNotFoundError('Output file doesn\'t exist for build_id: {} subjob_id: {} atom_id: {}'.format( build_id, subjob_id, atom_id)) try: console_output = ConsoleOutput(output_file) segment = console_output.segment(max_lines, offset_line) except ValueError as e: raise BadRequestError(e) return { 'offset_line': segment.offset_line, 'num_lines': segment.num_lines, 'total_num_lines': segment.total_num_lines, 'content': segment.content, }
def test_segment_raises_value_error_if_offset_greater_than_total_length( self): complete_output_path = self.create_temp_plaintext_file( _COMPLETE_OUTPUT) console_output = ConsoleOutput.from_plaintext(complete_output_path) with self.assertRaises(BadRequestError): console_output.segment(max_lines=5, offset_line=155)
def get_console_output( cls, build_id: int, subjob_id: int, atom_id: int, result_root: str, max_lines: int = 50, offset_line: Optional[int] = None, ) -> Optional[ConsoleOutputSegment]: """ Return the console output if it exists in the specified result_root. Return None if it does not exist. :param build_id: build id :param subjob_id: subjob id :param atom_id: atom id :param result_root: the sys path to either the results or artifacts directory where results are stored. :param max_lines: The maximum total number of lines to return. If this max_lines + offset_line lines do not exist in the output file, just return what there is. :param offset_line: The line number (0-indexed) to start reading content for. If none is specified, we will return the console output starting from the end of the file. :return: The console output if it exists in the specified result_root, None if it does not exist """ console_output = None artifact_dir = cls.atom_artifact_directory(build_id, subjob_id, atom_id, result_root=result_root) output_file_path = os.path.join(artifact_dir, cls.OUTPUT_FILE) if os.path.isfile(output_file_path): # Read directly from output file if it exists (while build is in progress). console_output = ConsoleOutput.from_plaintext(output_file_path) else: # Read from build artifact archive if it exists (after build is finished). build_dir = cls.build_artifact_directory(build_id, result_root=result_root) archive_file_path = os.path.join(build_dir, cls.ARTIFACT_ZIPFILE_NAME) if os.path.isfile(archive_file_path): path_in_archive = os.path.join( os.path.relpath(artifact_dir, build_dir), cls.OUTPUT_FILE) console_output = ConsoleOutput.from_zipfile( archive_file_path, path_in_archive) if console_output: return console_output.segment(max_lines, offset_line) return None
def test_console_output_segment_with_no_offset_from_zipfile_returns_expected(self): complete_output_path = self.create_temp_zip_file(_COMPLETE_OUTPUT, _PATH_IN_ARCHIVE) console_output = ConsoleOutput.from_zipfile(complete_output_path, _PATH_IN_ARCHIVE) segment = console_output.segment(max_lines=3) self.assertEquals(segment.num_lines, 3) self.assertEquals(segment.offset_line, 7) self.assertEquals(segment.total_num_lines, 10) self.assertEquals(segment.content, 'line_7\nline_8\nline_9\n')
def test_console_output_segment_with_offset_from_zipfile_returns_expected( self): complete_output_path = self.create_temp_zip_file( _COMPLETE_OUTPUT, _PATH_IN_ARCHIVE) console_output = ConsoleOutput.from_zipfile(complete_output_path, _PATH_IN_ARCHIVE) segment = console_output.segment(max_lines=3, offset_line=2) self.assertEquals(segment.num_lines, 3) self.assertEquals(segment.offset_line, 2) self.assertEquals(segment.total_num_lines, 10) self.assertEquals(segment.content, 'line_2\nline_3\nline_4\n')
def get_console_output( cls, build_id: int, subjob_id: int, atom_id: int, result_root: str, max_lines: int=50, offset_line: Optional[int]=None, ) -> Optional[ConsoleOutputSegment]: """ Return the console output if it exists in the specified result_root. Return None if it does not exist. :param build_id: build id :param subjob_id: subjob id :param atom_id: atom id :param result_root: the sys path to either the results or artifacts directory where results are stored. :param max_lines: The maximum total number of lines to return. If this max_lines + offset_line lines do not exist in the output file, just return what there is. :param offset_line: The line number (0-indexed) to start reading content for. If none is specified, we will return the console output starting from the end of the file. :return: The console output if it exists in the specified result_root, None if it does not exist """ console_output = None artifact_dir = cls.atom_artifact_directory(build_id, subjob_id, atom_id, result_root=result_root) output_file_path = os.path.join(artifact_dir, cls.OUTPUT_FILE) if os.path.isfile(output_file_path): # Read directly from output file if it exists (while build is in progress). console_output = ConsoleOutput.from_plaintext(output_file_path) else: # Read from build artifact archive if it exists (after build is finished). build_dir = cls.build_artifact_directory(build_id, result_root=result_root) archive_file_path = os.path.join(build_dir, cls.ARTIFACT_ZIPFILE_NAME) if os.path.isfile(archive_file_path): path_in_archive = os.path.join(os.path.relpath(artifact_dir, build_dir), cls.OUTPUT_FILE) console_output = ConsoleOutput.from_zipfile(archive_file_path, path_in_archive) if console_output: return console_output.segment(max_lines, offset_line) return None
def test_segment_for_incomplete_console_output( self, input_max_lines, input_offset_line, expected_num_lines, expected_offset_line, expected_total_num_lines, expected_content ): """ :type input_max_lines: int :type input_offset_line: int | None :type expected_num_lines: int :type expected_offset_line: int :type expected_total_num_lines: int :type expected_content: str """ console_output = ConsoleOutput(self._incomplete_console_output_file_path) segment = console_output.segment(max_lines=input_max_lines, offset_line=input_offset_line) self.assertEquals(segment.num_lines, expected_num_lines) self.assertEquals(segment.offset_line, expected_offset_line) self.assertEquals(segment.total_num_lines, expected_total_num_lines) self.assertEquals(segment.content, expected_content)
def test_segment_for_incomplete_console_output( self, input_max_lines: int, input_offset_line: Optional[int], expected_num_lines: int, expected_offset_line: int, expected_total_num_lines: int, expected_content: str, ): incomplete_output_path = self.create_temp_plaintext_file(_INCOMPLETE_OUTPUT) console_output = ConsoleOutput.from_plaintext(incomplete_output_path) segment = console_output.segment(max_lines=input_max_lines, offset_line=input_offset_line) self.assertEquals(segment.num_lines, expected_num_lines) self.assertEquals(segment.offset_line, expected_offset_line) self.assertEquals(segment.total_num_lines, expected_total_num_lines) self.assertEquals(segment.content, expected_content)
def test_segment_for_incomplete_console_output( self, input_max_lines: int, input_offset_line: Optional[int], expected_num_lines: int, expected_offset_line: int, expected_total_num_lines: int, expected_content: str, ): incomplete_output_path = self.create_temp_plaintext_file( _INCOMPLETE_OUTPUT) console_output = ConsoleOutput.from_plaintext(incomplete_output_path) segment = console_output.segment(max_lines=input_max_lines, offset_line=input_offset_line) self.assertEquals(segment.num_lines, expected_num_lines) self.assertEquals(segment.offset_line, expected_offset_line) self.assertEquals(segment.total_num_lines, expected_total_num_lines) self.assertEquals(segment.content, expected_content)
def test_segment_raises_value_error_if_offset_greater_than_total_length(self): with self.assertRaises(ValueError): console_output = ConsoleOutput(self._completed_console_output_file_path) console_output.segment(max_lines=5, offset_line=155)
def test_segment_raises_value_error_if_offset_greater_than_total_length(self): complete_output_path = self.create_temp_plaintext_file(_COMPLETE_OUTPUT) console_output = ConsoleOutput.from_plaintext(complete_output_path) with self.assertRaises(BadRequestError): console_output.segment(max_lines=5, offset_line=155)