def get_multiline(file, filename, text, annotation_start, annotation_end, position): """ Gets sourcerange and end position of an annotation that can span multiple lines. :param file: A tuple of strings, with each string being a line in the file. :param filename: The name of the file. :param annotation_start: The string specifying the start of the annotation. :param annotation_end: The string specifying the end of the annotation. :param position: An integer identifying the position where the annotation started. :return: A SourceRange object holding the range of the multi-line annotation and the end_position of the annotation as an integer. """ end_end = get_end_position(annotation_end, text, position + len(annotation_start) - 1) if end_end == -1: _range = SourceRange.from_absolute_position( filename, AbsolutePosition(file, position)) raise NoCloseError(annotation_start, _range) return (SourceRange.from_absolute_position( filename, AbsolutePosition(file, position), AbsolutePosition(file, end_end)), end_end)
def test_combined_strings(self): file_text = ['"some string #with comment"\n', '"""\n', "now a multiline string ''' <- this one not\n", '"""\n', '"""\n' 'another comment # rather harmless\n', '"""\n'] string1_start = 0 string1_end = len(file_text[0]) - 2 string1 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string1_start), AbsolutePosition(file_text, string1_end)) string2_start = string1_end+2 text = ''.join(file_text) string2_end = text.find('"""', string2_start + 1) + 2 #+2 for length of """ string2 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string2_start), AbsolutePosition(file_text, string2_end)) string3_start = text.find('"""', string2_end + 1) string3_end = text.find('"""', string3_start + 1) + 2 #+2 for length of """ string3 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string3_start), AbsolutePosition(file_text, string3_end)) with execute_bear(self.python_uut, "F", file_text) as results: self.assertIn(string1, results[0].contents['strings']) self.assertIn(string2, results[0].contents['strings']) self.assertIn(string3, results[0].contents['strings']) self.assertEqual(results[0].contents['comments'], ())
def test_combined_strings(self): file_text = [ '"some string #with comment"\n', '"""\n', "now a multiline string ''' <- this one not\n", '"""\n', '"""\n' 'another comment # rather harmless\n', '"""\n' ] string1_start = 0 string1_end = len(file_text[0]) - 2 string1 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string1_start), AbsolutePosition(file_text, string1_end)) string2_start = string1_end + 2 text = ''.join(file_text) string2_end = text.find('"""', string2_start + 1) + 2 #+2 for length of """ string2 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string2_start), AbsolutePosition(file_text, string2_end)) string3_start = text.find('"""', string2_end + 1) string3_end = text.find('"""', string3_start + 1) + 2 #+2 for length of """ string3 = SourceRange.from_absolute_position( "F", AbsolutePosition(file_text, string3_start), AbsolutePosition(file_text, string3_end)) with execute_bear(self.python_uut, "F", file_text) as results: self.assertIn(string1, results[0].contents['strings']) self.assertIn(string2, results[0].contents['strings']) self.assertIn(string3, results[0].contents['strings']) self.assertEqual(results[0].contents['comments'], ())
def get_singleline_strings(file, filename, text, string_start, string_end, position): """ Gets sourcerange of a single-line string and its end position. :param file: A tuple of strings, with each string being a line in the file. :param filename: The name of the file. :param string_start: The string which specifies how a string starts. :param string_end: The string which specifies how a string ends. :position: An integer identifying the position where the string started. :return: A SourceRange object identifying the range of the single-line string and the end_position of the string as an integer. """ end_position = get_end_position(string_end, text, position + len(string_start) - 1) newline = get_end_position("\n", text, position) if newline == -1: newline = len(text) if end_position == -1: _range = SourceRange.from_absolute_position( filename, AbsolutePosition(file, position)) raise NoCloseError(string_start, _range) if newline > end_position: return (SourceRange.from_absolute_position( filename, AbsolutePosition(file, position), AbsolutePosition(file, end_position)), end_position)
def test_from_absolute_position(self): text = ('a\n', 'b\n') start = AbsolutePosition(text, 0) end = AbsolutePosition(text, 2) uut = SourceRange.from_absolute_position('F', start, end) compare = SourceRange.from_values('F', 1, 1, 2, 1) self.assertEqual(uut, compare) uut = SourceRange.from_absolute_position('F', start, None) compare = SourceRange(SourcePosition('F', 1, 1), None) self.assertEqual(uut, compare)
def test_from_absolute_position(self): text = ("a\n", "b\n") start = AbsolutePosition(text, 0) end = AbsolutePosition(text, 2) uut = SourceRange.from_absolute_position("F", start, end) compare = SourceRange.from_values("F", 1, 1, 2, 1) self.assertEqual(uut, compare) uut = SourceRange.from_absolute_position("F", start, None) compare = SourceRange(SourcePosition("F", 1, 1), None) self.assertEqual(uut, compare)
def test_string_with_comments(self): text = ["some #comment\n", "with 'string' in next line"] comment_start = text[0].find('#') comment_end = len(text[0]) - 1 string_start = ''.join(text).find("'") string_end = ''.join(text).find("'", string_start + 1) compare = [(SourceRange.from_absolute_position( "F", AbsolutePosition(text, string_start), AbsolutePosition(text, string_end)), ), (SourceRange.from_absolute_position( "F", AbsolutePosition(text, comment_start), AbsolutePosition(text, comment_end)), )] with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare[0]) self.assertEqual(result[0].contents['comments'], compare[1])
def get_singleline_comment(file, filename, text, comment, position): """ Gets Sourcerange of a single-line comment where the start is the start of comment and the end is the end of line. :param file: A tuple of strings, with each string being a line in the file. :param filename: The name of the file. :param comment: The string which specifies the comment. :position: An integer identifying the position where the string started. :return: A SourceRange object identifying the range of the single-line comment and the end_position of the comment as an integer. """ end_position = get_end_position('\n', text, position + len(comment) - 1) if end_position == -1: end_position = len(text) - 1 return (SourceRange.from_absolute_position( filename, AbsolutePosition(file, position), AbsolutePosition(file, end_position)), end_position)
def test_string_with_comments(self): text = ["some #comment\n", "with 'string' in next line"] comment_start = text[0].find('#') comment_end = len(text[0]) - 1 string_start = ''.join(text).find("'") string_end = ''.join(text).find("'", string_start + 1) compare = [(SourceRange.from_absolute_position( "F", AbsolutePosition(text, string_start), AbsolutePosition(text, string_end)),), (SourceRange.from_absolute_position( "F", AbsolutePosition(text, comment_start), AbsolutePosition(text, comment_end)),)] with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare[0]) self.assertEqual(result[0].contents['comments'], compare[1])
def test_multiline_comment(self): text = ["some string /*within \n", "'multiline comment'*/"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, text[0].find('/*')), AbsolutePosition(text, len(''.join(text)) - 1)),) with execute_bear(self.c_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare)
def test_single_line_comment(self): text = ["some #coment with 'string'\n", "and next line"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, text[0].find('#')), AbsolutePosition(text, len(text[0]) - 1)),) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare)
def test_multiline_string(self): text = ["'''multiline string, #comment within it'''\n"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, 0), AbsolutePosition(text, len(text[0])-2)),) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare) self.assertEqual(result[0].contents['comments'], ())
def test_single_line_string(self): text = ["'from start till the end with #comments'\n", ] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, 0), AbsolutePosition(text, len(text[0]) - 2)),) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare) self.assertEqual(result[0].contents['comments'], ())
def get_specified_block_range(self, file, filename, open_specifier, close_specifier, annotation_dict): """ Gets a sourceranges of all the indentation blocks present inside the file. :param file: File that needs to be checked in the form of a list of strings. :param filename: Name of the file that needs to be checked. :param open_specifier: A character or string indicating that the block has begun. :param close_specifier: A character or string indicating that the block has ended. :param annotation_dict: A dictionary containing sourceranges of all the strings and comments within a file. :return: A tuple whith the first source range being the range of the outermost indentation while last being the range of the most nested/innermost indentation. Equal level indents appear in the order of first encounter or left to right. """ ranges = [] open_pos = list(self.get_valid_sequences( file, open_specifier, annotation_dict)) close_pos = list(self.get_valid_sequences( file, close_specifier, annotation_dict)) to_match = len(open_pos) - 1 while to_match >= 0: close_index = 0 while close_index < len(close_pos): if(open_pos[to_match].position <= close_pos[close_index].position): ranges.append( SourceRange.from_absolute_position( filename, open_pos[to_match], close_pos[close_index])) close_pos.remove(close_pos[close_index]) open_pos.remove(open_pos[to_match]) to_match -= 1 break close_index += 1 if((len(close_pos) == 0 and to_match != -1) or (len(close_pos) != 0 and to_match == -1)): # None to specify unmatched indents raise UnmatchedIndentError(open_specifier, close_specifier) # Ranges are returned in the order of least nested to most nested # and also on the basis of which come first return tuple(ranges)[::-1]
def test_multiline_comment(self): text = ["some string /*within \n", "'multiline comment'*/"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, text[0].find('/*')), AbsolutePosition(text, len(''.join(text)) - 1)), ) with execute_bear(self.c_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare)
def test_single_line_comment(self): text = ["some #coment with 'string'\n", "and next line"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, text[0].find('#')), AbsolutePosition(text, len(text[0]) - 1)), ) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare)
def test_multiline_string(self): text = ["'''multiline string, #comment within it'''\n"] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, 0), AbsolutePosition(text, len(text[0]) - 2)), ) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare) self.assertEqual(result[0].contents['comments'], ())
def test_single_line_string(self): text = [ "'from start till the end with #comments'\n", ] compare = (SourceRange.from_absolute_position( "F", AbsolutePosition(text, 0), AbsolutePosition(text, len(text[0]) - 2)), ) with execute_bear(self.python_uut, "F", text) as result: self.assertEqual(result[0].contents['strings'], compare) self.assertEqual(result[0].contents['comments'], ())
def get_singleline_strings(file, filename, text, string_start, string_end, position): """ Gets sourcerange of a single-line string and its end position. :param file: A tuple of strings, with each string being a line in the file. :param filename: The name of the file. :param string_start: The string which specifies how a string starts. :param string_end: The string which specifies how a string ends. :position: An integer identifying the position where the string started. :return: A SourceRange object identifying the range of the single-line string and the end_position of the string as an integer. """ end_position = get_end_position(string_end, text, position + len(string_start) - 1) newline = get_end_position('\n', text, position) if newline == -1: newline = len(text) if end_position == -1: _range = SourceRange.from_absolute_position( filename, AbsolutePosition(file, position)) raise NoCloseError(string_start, _range) if newline > end_position: return (SourceRange.from_absolute_position( filename, AbsolutePosition(file, position), AbsolutePosition(file, end_position)), end_position)
def test_multiline_comment(self): text = ['some string /*within \n', "'multiline comment'*/"] compare = (SourceRange.from_absolute_position( 'F', AbsolutePosition(text, text[0].find('/*')), AbsolutePosition(text, len(''.join(text)) - 1)), ) with execute_bear(self.c_uut, 'F', text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare) text = ['/*Multiline which does not end'] with execute_bear(self.c_uut, 'F', text) as result: self.assertEqual(result[0].message, '/* has no closure')
def test_multiline_comment(self): text = ['some string /*within \n', "'multiline comment'*/"] compare = (SourceRange.from_absolute_position( 'F', AbsolutePosition(text, text[0].find('/*')), AbsolutePosition(text, len(''.join(text)) - 1)),) with execute_bear(self.c_uut, 'F', text) as result: self.assertEqual(result[0].contents['strings'], ()) self.assertEqual(result[0].contents['comments'], compare) text = ['/*Multiline which does not end'] with execute_bear(self.c_uut, 'F', text) as result: self.assertEqual(result[0].message, '/* has no closure')
def test_escape_strings(self): text = [r"'I\'ll be back' -T1000"] uut = AnnotationBear(self.section1, Queue()) test_range = SourceRange.from_absolute_position( 'F', AbsolutePosition(text, 0), AbsolutePosition(text, text[0].find("'", 4))) with execute_bear(uut, 'F', text) as result: self.assertEqual(result[0].contents['strings'], (test_range,)) text = [''' """"quoting inside quoting" """ '''] uut = AnnotationBear(self.section1, Queue()) with execute_bear(uut, 'F', text) as results: for result in results: # The """" was recognized as a string start and end before. # That lead to a Result being yielded because of unclosed # quotes, this asserts that no such thing happened. self.assertEqual(type(result), HiddenResult)
def find_with_start_end(filename, file, annot): """ Gives all positions of annotations which have a start and end. :param filename: Name of the file on which the bear is running. :param file: Contents of the file in the form of tuple of lines. :param annot: A dict containing start of annotation as key and end of annotation as value. :return: A set of SourceRanges giving the range of annotation. """ text = ''.join(file) found_pos = set() for annot_type in annot: found_pos.update(unescaped_search_in_between( annot_type, annot[annot_type], text)) if found_pos: found_pos = set(SourceRange.from_absolute_position( filename, AbsolutePosition(file, position.begin.range[0]), AbsolutePosition( file, position.end.range[1] - 1)) for position in found_pos) return found_pos
def find_singleline_comments(filename, file, comments): """ Finds all single-line comments. :param filename: Name of the file on which the bear is running. :param file: Contents of the file in the form of tuple of lines. :param comments: A list containing different types of single-line comments. :return: A set of SourceRange objects with start as the beginning of the comment and end as the termination of line. """ text = ''.join(file) single_comments = set() for comment_type in comments: for found in unescaped_search_for(comment_type, text): start = found.start() end = text.find('\n', start) end = len(text) - 1 if end == -1 else end single_comments.add(SourceRange.from_absolute_position( filename, AbsolutePosition(file, start), AbsolutePosition(file, end))) return single_comments