<s> import copy <EOL> import re <EOL> try : <EOL> from lxml import etree as lxml_etree <EOL> except ImportError : <EOL> lxml_etree = None <EOL> from robotide . lib . robot . api import logger <EOL> from robotide . lib . robot . libraries . BuiltIn import BuiltIn <EOL> from robotide . lib . robot . utils import ( asserts , ET , ETSource , is_string , is_truthy , <EOL> plural_or_not as s ) <EOL> from robotide . lib . robot . version import get_version <EOL> should_be_equal = asserts . assert_equals <EOL> should_match = BuiltIn ( ) . should_match <EOL> class XML ( object ) : <EOL> """<STR_LIT>""" <EOL> ROBOT_LIBRARY_SCOPE = '<STR_LIT>' <EOL> ROBOT_LIBRARY_VERSION = get_version ( ) <EOL> _whitespace = re . compile ( '<STR_LIT>' ) <EOL> _xml_declaration = re . compile ( '<STR_LIT>' ) <EOL> def __init__ ( self , use_lxml = False ) : <EOL> """<STR_LIT>""" <EOL> use_lxml = is_truthy ( use_lxml ) <EOL> if use_lxml and lxml_etree : <EOL> self . etree = lxml_etree <EOL> self . modern_etree = True <EOL> self . lxml_etree = True <EOL> else : <EOL> self . etree = ET <EOL> self . modern_etree = ET . VERSION >= '<STR_LIT>' <EOL> self . lxml_etree = False <EOL> if use_lxml and not lxml_etree : <EOL> logger . warn ( '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> def parse_xml ( self , source , keep_clark_notation = False ) : <EOL> """<STR_LIT>""" <EOL> with ETSource ( source ) as source : <EOL> root = self . etree . parse ( source ) . getroot ( ) <EOL> if self . lxml_etree : <EOL> self . _remove_comments ( root ) <EOL> if not is_truthy ( keep_clark_notation ) : <EOL> NameSpaceStripper ( ) . strip ( root ) <EOL> return root <EOL> def _remove_comments ( self , node ) : <EOL> for comment in node . xpath ( '<STR_LIT>' ) : <EOL> parent = comment . getparent ( ) <EOL> if parent : <EOL> self . _preserve_tail ( comment , parent ) <EOL> parent . remove ( comment ) <EOL> def get_element ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> elements = self . get_elements ( source , xpath ) <EOL> if len ( elements ) != <NUM_LIT:1> : <EOL> self . _raise_wrong_number_of_matches ( len ( elements ) , xpath ) <EOL> return elements [ <NUM_LIT:0> ] <EOL> def _raise_wrong_number_of_matches ( self , count , xpath , message = None ) : <EOL> if not message : <EOL> message = self . _wrong_number_of_matches ( count , xpath ) <EOL> raise AssertionError ( message ) <EOL> def _wrong_number_of_matches ( self , count , xpath ) : <EOL> if not count : <EOL> return "<STR_LIT>" % xpath <EOL> if count == <NUM_LIT:1> : <EOL> return "<STR_LIT>" % xpath <EOL> return "<STR_LIT>" % ( count , xpath ) <EOL> def get_elements ( self , source , xpath ) : <EOL> """<STR_LIT>""" <EOL> if is_string ( source ) : <EOL> source = self . parse_xml ( source ) <EOL> finder = ElementFinder ( self . etree , self . modern_etree , self . lxml_etree ) <EOL> return finder . find_all ( source , xpath ) <EOL> def get_child_elements ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> return list ( self . get_element ( source , xpath ) ) <EOL> def get_element_count ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> count = len ( self . get_elements ( source , xpath ) ) <EOL> logger . info ( "<STR_LIT>" % ( count , s ( count ) , xpath ) ) <EOL> return count <EOL> def element_should_exist ( self , source , xpath = '<STR_LIT:.>' , message = None ) : <EOL> """<STR_LIT>""" <EOL> count = self . get_element_count ( source , xpath ) <EOL> if not count : <EOL> self . _raise_wrong_number_of_matches ( count , xpath , message ) <EOL> def element_should_not_exist ( self , source , xpath = '<STR_LIT:.>' , message = None ) : <EOL> """<STR_LIT>""" <EOL> count = self . get_element_count ( source , xpath ) <EOL> if count : <EOL> self . _raise_wrong_number_of_matches ( count , xpath , message ) <EOL> def get_element_text ( self , source , xpath = '<STR_LIT:.>' , normalize_whitespace = False ) : <EOL> """<STR_LIT>""" <EOL> element = self . get_element ( source , xpath ) <EOL> text = '<STR_LIT>' . join ( self . _yield_texts ( element ) ) <EOL> if is_truthy ( normalize_whitespace ) : <EOL> text = self . _normalize_whitespace ( text ) <EOL> return text <EOL> def _yield_texts ( self , element , top = True ) : <EOL> if element . text : <EOL> yield element . text <EOL> for child in element : <EOL> for text in self . _yield_texts ( child , top = False ) : <EOL> yield text <EOL> if element . tail and not top : <EOL> yield element . tail <EOL> def _normalize_whitespace ( self , text ) : <EOL> return self . _whitespace . sub ( '<STR_LIT:U+0020>' , text . strip ( ) ) <EOL> def get_elements_texts ( self , source , xpath , normalize_whitespace = False ) : <EOL> """<STR_LIT>""" <EOL> return [ self . get_element_text ( elem , normalize_whitespace = normalize_whitespace ) <EOL> for elem in self . get_elements ( source , xpath ) ] <EOL> def element_text_should_be ( self , source , expected , xpath = '<STR_LIT:.>' , <EOL> normalize_whitespace = False , message = None ) : <EOL> """<STR_LIT>""" <EOL> text = self . get_element_text ( source , xpath , normalize_whitespace ) <EOL> should_be_equal ( text , expected , message , values = False ) <EOL> def element_text_should_match ( self , source , pattern , xpath = '<STR_LIT:.>' , <EOL> normalize_whitespace = False , message = None ) : <EOL> """<STR_LIT>""" <EOL> text = self . get_element_text ( source , xpath , normalize_whitespace ) <EOL> should_match ( text , pattern , message , values = False ) <EOL> def get_element_attribute ( self , source , name , xpath = '<STR_LIT:.>' , default = None ) : <EOL> """<STR_LIT>""" <EOL> return self . get_element ( source , xpath ) . get ( name , default ) <EOL> def get_element_attributes ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> return dict ( self . get_element ( source , xpath ) . attrib ) <EOL> def element_attribute_should_be ( self , source , name , expected , xpath = '<STR_LIT:.>' , <EOL> message = None ) : <EOL> """<STR_LIT>""" <EOL> attr = self . get_element_attribute ( source , name , xpath ) <EOL> should_be_equal ( attr , expected , message , values = False ) <EOL> def element_attribute_should_match ( self , source , name , pattern , xpath = '<STR_LIT:.>' , <EOL> message = None ) : <EOL> """<STR_LIT>""" <EOL> attr = self . get_element_attribute ( source , name , xpath ) <EOL> if attr is None : <EOL> raise AssertionError ( "<STR_LIT>" % name ) <EOL> should_match ( attr , pattern , message , values = False ) <EOL> def element_should_not_have_attribute ( self , source , name , xpath = '<STR_LIT:.>' , message = None ) : <EOL> """<STR_LIT>""" <EOL> attr = self . get_element_attribute ( source , name , xpath ) <EOL> if attr is not None : <EOL> raise AssertionError ( message or "<STR_LIT>" <EOL> "<STR_LIT>" % ( name , attr ) ) <EOL> def elements_should_be_equal ( self , source , expected , exclude_children = False , <EOL> normalize_whitespace = False ) : <EOL> """<STR_LIT>""" <EOL> self . _compare_elements ( source , expected , should_be_equal , <EOL> exclude_children , normalize_whitespace ) <EOL> def elements_should_match ( self , source , expected , exclude_children = False , <EOL> normalize_whitespace = False ) : <EOL> """<STR_LIT>""" <EOL> self . _compare_elements ( source , expected , should_match , <EOL> exclude_children , normalize_whitespace ) <EOL> def _compare_elements ( self , source , expected , comparator , exclude_children , <EOL> normalize_whitespace ) : <EOL> normalizer = self . _normalize_whitespace if is_truthy ( normalize_whitespace ) else None <EOL> comparator = ElementComparator ( comparator , normalizer , exclude_children ) <EOL> comparator . compare ( self . get_element ( source ) , self . get_element ( expected ) ) <EOL> def set_element_tag ( self , source , tag , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> self . get_element ( source , xpath ) . tag = tag <EOL> return source <EOL> def set_elements_tag ( self , source , tag , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> for elem in self . get_elements ( source , xpath ) : <EOL> self . set_element_tag ( elem , tag ) <EOL> def set_element_text ( self , source , text = None , tail = None , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> element = self . get_element ( source , xpath ) <EOL> if text is not None : <EOL> element . text = text <EOL> if tail is not None : <EOL> element . tail = tail <EOL> return source <EOL> def set_elements_text ( self , source , text = None , tail = None , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> for elem in self . get_elements ( source , xpath ) : <EOL> self . set_element_text ( elem , text , tail ) <EOL> def set_element_attribute ( self , source , name , value , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> if not name : <EOL> raise RuntimeError ( '<STR_LIT>' ) <EOL> source = self . get_element ( source ) <EOL> self . get_element ( source , xpath ) . attrib [ name ] = value <EOL> return source <EOL> def set_elements_attribute ( self , source , name , value , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> for elem in self . get_elements ( source , xpath ) : <EOL> self . set_element_attribute ( elem , name , value ) <EOL> def remove_element_attribute ( self , source , name , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> attrib = self . get_element ( source , xpath ) . attrib <EOL> if name in attrib : <EOL> attrib . pop ( name ) <EOL> return source <EOL> def remove_elements_attribute ( self , source , name , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> for elem in self . get_elements ( source , xpath ) : <EOL> self . remove_element_attribute ( elem , name ) <EOL> def remove_element_attributes ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> self . get_element ( source , xpath ) . attrib . clear ( ) <EOL> return source <EOL> def remove_elements_attributes ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> for elem in self . get_elements ( source , xpath ) : <EOL> self . remove_element_attributes ( elem ) <EOL> def add_element ( self , source , element , index = None , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> parent = self . get_element ( source , xpath ) <EOL> element = self . copy_element ( element ) <EOL> if index is None : <EOL> parent . append ( element ) <EOL> else : <EOL> parent . insert ( int ( index ) , element ) <EOL> return source <EOL> def remove_element ( self , source , xpath = '<STR_LIT>' , remove_tail = False ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> self . _remove_element ( source , self . get_element ( source , xpath ) , remove_tail ) <EOL> return source <EOL> def remove_elements ( self , source , xpath = '<STR_LIT>' , remove_tail = False ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> for element in self . get_elements ( source , xpath ) : <EOL> self . _remove_element ( source , element , remove_tail ) <EOL> return source <EOL> def _remove_element ( self , root , element , remove_tail = False ) : <EOL> parent = self . _find_parent ( root , element ) <EOL> if not is_truthy ( remove_tail ) : <EOL> self . _preserve_tail ( element , parent ) <EOL> parent . remove ( element ) <EOL> def _find_parent ( self , root , element ) : <EOL> for parent in root . getiterator ( ) : <EOL> for child in parent : <EOL> if child is element : <EOL> return parent <EOL> raise RuntimeError ( '<STR_LIT>' ) <EOL> def _preserve_tail ( self , element , parent ) : <EOL> if not element . tail : <EOL> return <EOL> index = list ( parent ) . index ( element ) <EOL> if index == <NUM_LIT:0> : <EOL> parent . text = ( parent . text or '<STR_LIT>' ) + element . tail <EOL> else : <EOL> sibling = parent [ index - <NUM_LIT:1> ] <EOL> sibling . tail = ( sibling . tail or '<STR_LIT>' ) + element . tail <EOL> def clear_element ( self , source , xpath = '<STR_LIT:.>' , clear_tail = False ) : <EOL> """<STR_LIT>""" <EOL> source = self . get_element ( source ) <EOL> element = self . get_element ( source , xpath ) <EOL> tail = element . tail <EOL> element . clear ( ) <EOL> if not is_truthy ( clear_tail ) : <EOL> element . tail = tail <EOL> return source <EOL> def copy_element ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> return copy . deepcopy ( self . get_element ( source , xpath ) ) <EOL> def element_to_string ( self , source , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> string = self . etree . tostring ( self . get_element ( source , xpath ) , encoding = '<STR_LIT>' ) <EOL> return self . _xml_declaration . sub ( '<STR_LIT>' , string . decode ( '<STR_LIT>' ) ) . strip ( ) <EOL> def log_element ( self , source , level = '<STR_LIT>' , xpath = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> string = self . element_to_string ( source , xpath ) <EOL> logger . write ( string , level ) <EOL> return string <EOL> def save_xml ( self , source , path , encoding = '<STR_LIT>' ) : <EOL> """<STR_LIT>""" <EOL> elem = self . get_element ( source ) <EOL> if self . lxml_etree : <EOL> NameSpaceStripper ( ) . unstrip ( elem ) <EOL> tree = self . etree . ElementTree ( elem ) <EOL> xml_declaration = { '<STR_LIT>' : True } if self . modern_etree else { } <EOL> with open ( path , '<STR_LIT:w>' ) as output : <EOL> tree . write ( output , encoding = encoding , ** xml_declaration ) <EOL> def evaluate_xpath ( self , source , expression , context = '<STR_LIT:.>' ) : <EOL> """<STR_LIT>""" <EOL> if not self . lxml_etree : <EOL> raise RuntimeError ( "<STR_LIT>" ) <EOL> return self . get_element ( source , context ) . xpath ( expression ) <EOL> class NameSpaceStripper ( object ) : <EOL> def strip ( self , elem , current_ns = None ) : <EOL> if elem . tag . startswith ( '<STR_LIT:{>' ) and '<STR_LIT:}>' in elem . tag : <EOL> ns , elem . tag = elem . tag [ <NUM_LIT:1> : ] . split ( '<STR_LIT:}>' , <NUM_LIT:1> ) <EOL> if ns != current_ns : <EOL> elem . attrib [ '<STR_LIT>' ] = ns <EOL> current_ns = ns <EOL> elif current_ns : <EOL> elem . attrib [ '<STR_LIT>' ] = '<STR_LIT>' <EOL> current_ns = None <EOL> for child in elem : <EOL> self . strip ( child , current_ns ) <EOL> def unstrip ( self , elem , current_ns = None ) : <EOL> ns = elem . attrib . pop ( '<STR_LIT>' , current_ns ) <EOL> if ns : <EOL> elem . tag = '<STR_LIT>' % ( ns , elem . tag ) <EOL> for child in elem : <EOL> self . unstrip ( child , ns ) <EOL> class ElementFinder ( object ) : <EOL> def __init__ ( self , etree , modern = True , lxml = False ) : <EOL> self . etree = etree <EOL> self . modern = modern <EOL> self . lxml = lxml <EOL> def find_all ( self , elem , xpath ) : <EOL> xpath = self . _get_xpath ( xpath ) <EOL> if xpath == '<STR_LIT:.>' : <EOL> return [ elem ] <EOL> if not self . lxml : <EOL> return elem . findall ( xpath ) <EOL> finder = self . etree . ETXPath ( xpath ) <EOL> return finder ( elem ) <EOL> def _get_xpath ( self , xpath ) : <EOL> if not xpath : <EOL> raise RuntimeError ( '<STR_LIT>' ) <EOL> if self . modern : <EOL> return xpath <EOL> try : <EOL> return str ( xpath ) <EOL> except UnicodeError : <EOL> if not xpath . replace ( '<STR_LIT:/>' , '<STR_LIT>' ) . isalnum ( ) : <EOL> logger . warn ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' ) <EOL> return xpath <EOL> class ElementComparator ( object ) : <EOL> def __init__ ( self , comparator , normalizer = None , exclude_children = False ) : <EOL> self . _comparator = comparator <EOL> self . _normalizer = normalizer or ( lambda text : text ) <EOL> self . _exclude_children = is_truthy ( exclude_children ) <EOL> def compare ( self , actual , expected , location = None ) : <EOL> if not location : <EOL> location = Location ( actual . tag ) <EOL> self . _compare_tags ( actual , expected , location ) <EOL> self . _compare_attributes ( actual , expected , location ) <EOL> self . _compare_texts ( actual , expected , location ) <EOL> if location . is_not_root : <EOL> self . _compare_tails ( actual , expected , location ) <EOL> if not self . _exclude_children : <EOL> self . _compare_children ( actual , expected , location ) <EOL> def _compare_tags ( self , actual , expected , location ) : <EOL> self . _compare ( actual . tag , expected . tag , '<STR_LIT>' , location , <EOL> should_be_equal ) <EOL> def _compare ( self , actual , expected , message , location , comparator = None ) : <EOL> if location . is_not_root : <EOL> message = "<STR_LIT>" % ( message , location . path ) <EOL> if not comparator : <EOL> comparator = self . _comparator <EOL> comparator ( actual , expected , message ) <EOL> def _compare_attributes ( self , actual , expected , location ) : <EOL> self . _compare ( sorted ( actual . attrib ) , sorted ( expected . attrib ) , <EOL> '<STR_LIT>' , location , should_be_equal ) <EOL> for key in actual . attrib : <EOL> self . _compare ( actual . attrib [ key ] , expected . attrib [ key ] , <EOL> "<STR_LIT>" % key , location ) <EOL> def _compare_texts ( self , actual , expected , location ) : <EOL> self . _compare ( self . _text ( actual . text ) , self . _text ( expected . text ) , <EOL> '<STR_LIT>' , location ) <EOL> def _text ( self , text ) : <EOL> return self . _normalizer ( text or '<STR_LIT>' ) <EOL> def _compare_tails ( self , actual , expected , location ) : <EOL> self . _compare ( self . _text ( actual . tail ) , self . _text ( expected . tail ) , <EOL> '<STR_LIT>' , location ) <EOL> def _compare_children ( self , actual , expected , location ) : <EOL> self . _compare ( len ( actual ) , len ( expected ) , '<STR_LIT>' , <EOL> location , should_be_equal ) <EOL> for act , exp in zip ( actual , expected ) : <EOL> self . compare ( act , exp , location . child ( act . tag ) ) <EOL> class Location ( object ) : <EOL> def __init__ ( self , path , is_root = True ) : <EOL> self . path = path <EOL> self . is_not_root = not is_root <EOL> self . _children = { } <EOL> def child ( self , tag ) : <EOL> if tag not in self . _children : <EOL> self . _children [ tag ] = <NUM_LIT:1> <EOL> else : <EOL> self . _children [ tag ] += <NUM_LIT:1> <EOL> tag += '<STR_LIT>' % self . _children [ tag ] <EOL> return Location ( '<STR_LIT>' % ( self . path , tag ) , is_root = False ) </s>
