def start(self, name, attrs, nsmap={}): # Make sure attrs is a mutable dict--lxml may send an immutable dictproxy. attrs = dict(attrs) tagnamespace, tagname = self._getNsTag(name) tagnsprefix = self._prefix_for_namespace(tagnamespace) # Invert each namespace map as it comes in. if len(nsmap) > 0: # A new namespace mapping has come into play. inverted_nsmap = dict( (value, key) for key, value in list(nsmap.items())) self.nsmaps.append(inverted_nsmap) # Also treat the namespace mapping as a set of attributes on the # tag, so we can recreate it later. attrs = attrs.copy() for prefix, namespace in list(nsmap.items()): attribute = NamespacedAttribute( "xmlns", prefix, "http://www.w3.org/2000/xmlns/") attrs[attribute] = namespace elif len(self.nsmaps) > 1: # There are no new namespaces for this tag, but # non-default namespaces are in play, so we need a # separate tag stack to know when they end. self.nsmaps.append(None) # Namespaces are in play. Find any attributes that came in # from lxml with namespaces attached to their names, and # turn then into NamespacedAttribute objects. new_attrs = {} for attr, value in list(attrs.items()): namespace, attr = self._getNsTag(attr) if namespace is None: new_attrs[attr] = value else: nsprefix = self._prefix_for_namespace(namespace) attr = NamespacedAttribute(nsprefix, attr, namespace) new_attrs[attr] = value attrs = new_attrs self.soup.handle_starttag(tagname, tagnamespace, tagnsprefix, attrs)
def setAttributes(self, attributes): if attributes is not None and attributes != {}: for name, value in list(attributes.items()): if isinstance(name, tuple): name = NamespacedAttribute(*name) self.element[name] = value # The attributes may contain variables that need substitution. # Call set_up_substitutions manually. # # The Tag constructor called this method when the Tag was created, # but we just set/changed the attributes, so call it again. self.element.contains_substitutions = ( self.soup.builder.set_up_substitutions(self.element))
def setAttributes(self, attributes): if attributes is not None and len(attributes) > 0: converted_attributes = [] for name, value in list(attributes.items()): if isinstance(name, tuple): new_name = NamespacedAttribute(*name) del attributes[name] attributes[new_name] = value self.soup.builder._replace_cdata_list_attribute_values( self.name, attributes) for name, value in list(attributes.items()): self.element[name] = value # The attributes may contain variables that need substitution. # Call set_up_substitutions manually. # # The Tag constructor called this method when the Tag was created, # but we just set/changed the attributes, so call it again. self.soup.builder.set_up_substitutions(self.element)
def test_attribute_is_equivalent_to_colon_separated_string(self): a = NamespacedAttribute("a", "b") self.assertEqual("a:b", a)
def test_name_may_be_none(self): a = NamespacedAttribute("xmlns", None) self.assertEqual(a, "xmlns")
def test_namespace_may_be_none_or_missing(self): a = NamespacedAttribute(None, "tag") assert a == "tag" a = NamespacedAttribute("", "tag") assert a == "tag"
def start(self, name, attrs, nsmap={}): # Make sure attrs is a mutable dict--lxml may send an immutable dictproxy. attrs = dict(attrs) nsprefix = None # Invert each namespace map as it comes in. if len(nsmap) == 0 and len(self.nsmaps) > 1: # There are no new namespaces for this tag, but # non-default namespaces are in play, so we need a # separate tag stack to know when they end. self.nsmaps.append(None) elif len(nsmap) > 0: # A new namespace mapping has come into play. # First, Let the BeautifulSoup object know about it. self._register_namespaces(nsmap) # Then, add it to our running list of inverted namespace # mappings. self.nsmaps.append(_invert(nsmap)) # The currently active namespace prefixes have # changed. Calculate the new mapping so it can be stored # with all Tag objects created while these prefixes are in # scope. current_mapping = dict(self.active_namespace_prefixes[-1]) current_mapping.update(nsmap) # We should not track un-prefixed namespaces as we can only hold one # and it will be recognized as the default namespace by soupsieve, # which may be confusing in some situations. if '' in current_mapping: del current_mapping[''] self.active_namespace_prefixes.append(current_mapping) # Also treat the namespace mapping as a set of attributes on the # tag, so we can recreate it later. attrs = attrs.copy() for prefix, namespace in list(nsmap.items()): attribute = NamespacedAttribute( "xmlns", prefix, "http://www.w3.org/2000/xmlns/") attrs[attribute] = namespace # Namespaces are in play. Find any attributes that came in # from lxml with namespaces attached to their names, and # turn then into NamespacedAttribute objects. new_attrs = {} for attr, value in list(attrs.items()): namespace, attr = self._getNsTag(attr) if namespace is None: new_attrs[attr] = value else: nsprefix = self._prefix_for_namespace(namespace) attr = NamespacedAttribute(nsprefix, attr, namespace) new_attrs[attr] = value attrs = new_attrs namespace, name = self._getNsTag(name) nsprefix = self._prefix_for_namespace(namespace) self.soup.handle_starttag( name, namespace, nsprefix, attrs, namespaces=self.active_namespace_prefixes[-1])
def test_namespace_may_be_none_or_missing(self): a = NamespacedAttribute(None, "tag") self.assertEqual(a, "tag") a = NamespacedAttribute("", "tag") self.assertEqual(a, "tag")