Repository: streamlink/streamlink
Pull request #2067 (2018-09-16 10:35:04)

utils.parse_xml: allow a tab for ignore_ns
A playlist has tabs instead of spaces,
`self.node` is `{urn:mpeg:dash:schema:mpd:2011}mpd` instead of `mpd`,
because `ignore_ns` did not work.

```
Traceback (most recent call last):
  File "env/bin/streamlink", line 11, in <module>
    load_entry_point('streamlink', 'console_scripts', 'streamlink')()
  File "src/streamlink_cli/main.py", line 1007, in main
    handle_url()
  File "src/streamlink_cli/main.py", line 557, in handle_url
    streams = fetch_streams(plugin)
  File "src/streamlink_cli/main.py", line 437, in fetch_streams
    sorting_excludes=args.stream_sorting_excludes)
  File "src/streamlink/plugin/plugin.py", line 317, in streams
    ostreams = self._get_streams()
  File "src/streamlink/plugins/dash.py", line 59, in _get_streams
    return DASHStream.parse_manifest(self.session, mpdurl)
  File "src/streamlink/stream/dash.py", line 180, in parse_manifest
    mpd = MPD(session.http.xml(res, ignore_ns=True), base_url=urlunparse(urlp), url=url)
  File "src/streamlink/stream/dash_manifest.py", line 229, in __init__
    super(MPD, self).__init__(node, root=self, *args, **kwargs)
  File "src/streamlink/stream/dash_manifest.py", line 151, in __init__
    raise MPDParsingError("root tag did not match the expected tag: {}".format(self.__tag__))
streamlink.stream.dash_manifest.MPDParsingError: root tag did not match the expected tag: MPD
```

---

`DeprecationWarning: Please use assertEqual instead.`

---

Flake8 import errors


Patch:
diff --git a/src/streamlink/utils/__init__.py b/src/streamlink/utils/__init__.py
index 25899edd..1d5018b5 100644
--- a/src/streamlink/utils/__init__.py
+++ b/src/streamlink/utils/__init__.py
@@ -85,7 +85,7 @@ def parse_xml(data, name="XML", ignore_ns=False, exception=PluginError, schema=N
         data = bytearray(data, "utf8")
 
     if ignore_ns:
-        data = re.sub(br" xmlns=\"(.+?)\"", b"", data)
+        data = re.sub(br"[\t ]xmlns=\"(.+?)\"", b"", data)
 
     if invalid_char_entities:
         data = re.sub(br'&(?!(?:#(?:[0-9]+|[Xx][0-9A-Fa-f]+)|[A-Za-z0-9]+);)', b'&amp;', data)


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index b9827864..3d3efdc6 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,9 +1,9 @@
 import base64
-import sys
 import os.path
+import sys
+import unittest
 
 from streamlink.plugin.api.validate import xml_element, text
-from streamlink.utils import update_scheme, url_equal, search_dict, load_module
 
 try:
     import xml.etree.cElementTree as ET
@@ -11,9 +11,18 @@ except ImportError:
     import xml.etree.ElementTree as ET
 from streamlink import PluginError
 from streamlink.plugin.api import validate
-from streamlink.utils import *
-
-import unittest
+from streamlink.utils import (
+    absolute_url,
+    load_module,
+    parse_json,
+    parse_qsd,
+    parse_xml,
+    prepend_www,
+    rtmpparse,
+    search_dict,
+    swfdecompress,
+    verifyjson,
+)
 
 # used in the import test to verify that this module was imported
 __test_marker__ = "test_marker"
@@ -59,6 +68,12 @@ class TestUtil(unittest.TestCase):
         self.assertEqual(expected.tag, actual.tag)
         self.assertEqual(expected.attrib, actual.attrib)
 
+    def test_parse_xml_ns_ignore_tab(self):
+        expected = ET.Element("test", {"foo": "bar"})
+        actual = parse_xml(u"""<test	foo="bar"	xmlns="foo:bar"/>""", ignore_ns=True)
+        self.assertEqual(expected.tag, actual.tag)
+        self.assertEqual(expected.attrib, actual.attrib)
+
     def test_parse_xml_ns(self):
         expected = ET.Element("{foo:bar}test", {"foo": "bar"})
         actual = parse_xml(u"""<h:test foo="bar" xmlns:h="foo:bar"/>""")
@@ -82,7 +97,6 @@ class TestUtil(unittest.TestCase):
         self.assertRaises(PluginError,
                           parse_xml, u"""<test foo="bar &"/>""")
 
-
     def test_parse_xml_entities(self):
         expected = ET.Element("test", {"foo": "bar &"})
         actual = parse_xml(u"""<test foo="bar &"/>""",
@@ -91,20 +105,19 @@ class TestUtil(unittest.TestCase):
         self.assertEqual(expected.tag, actual.tag)
         self.assertEqual(expected.attrib, actual.attrib)
 
-
     def test_parse_qsd(self):
         self.assertEqual(
             {"test": "1", "foo": "bar"},
             parse_qsd("test=1&foo=bar", schema=validate.Schema({"test": validate.text, "foo": "bar"})))
 
     def test_rtmpparse(self):
-        self.assertEquals(
+        self.assertEqual(
             ("rtmp://testserver.com:1935/app", "playpath?arg=1"),
             rtmpparse("rtmp://testserver.com/app/playpath?arg=1"))
-        self.assertEquals(
+        self.assertEqual(
             ("rtmp://testserver.com:1935/long/app", "playpath?arg=1"),
             rtmpparse("rtmp://testserver.com/long/app/playpath?arg=1"))
-        self.assertEquals(
+        self.assertEqual(
             ("rtmp://testserver.com:1935/app", None),
             rtmpparse("rtmp://testserver.com/app"))