nodes: 0, module; 1, function_definition; 2, function_name:add_tag; 3, parameters; 4, block; 5, identifier:self; 6, identifier:tag; 7, identifier:value; 8, expression_statement; 9, expression_statement; 10, expression_statement; 11, if_statement; 12, if_statement; 13, comment:""" as tags are kept in a sorted order, a bisection is a fastest way to identify a correct position
          of or a new tag to be added. An additional check is required to make sure w don't add duplicates
          """; 14, assignment; 15, assignment; 16, comparison_operator:index < len(self.tags); 17, block; 18, not_operator; 19, block; 20, identifier:index; 21, call; 22, identifier:contains; 23, False; 24, identifier:index; 25, call; 26, expression_statement; 27, identifier:contains; 28, expression_statement; 29, identifier:bisect_left; 30, argument_list; 31, identifier:len; 32, argument_list; 33, assignment; 34, call; 35, attribute; 36, tuple; 37, attribute; 38, identifier:contains; 39, comparison_operator:self.tags[index] == (tag, value); 40, attribute; 41, argument_list; 42, identifier:self; 43, identifier:tags; 44, identifier:tag; 45, identifier:value; 46, identifier:self; 47, identifier:tags; 48, subscript; 49, tuple; 50, attribute; 51, identifier:insert; 52, identifier:index; 53, tuple; 54, attribute; 55, identifier:index; 56, identifier:tag; 57, identifier:value; 58, identifier:self; 59, identifier:tags; 60, identifier:tag; 61, identifier:value; 62, identifier:self; 63, identifier:tags
code: def add_tag(self, tag, value):
          """ as tags are kept in a sorted order, a bisection is a fastest way to identify a correct position
          of or a new tag to be added. An additional check is required to make sure w don't add duplicates
          """
          index = bisect_left(self.tags, (tag, value))
          contains = False
          if index < len(self.tags):
              contains = self.tags[index] == (tag, value)
          if not contains:
              self.tags.insert(index, (tag, value))