Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,8 @@ dmypy.json
.vscode

# Pycharm
.idea
.idea

# Windows python venv
/Scripts/
pyvenv.cfg
3 changes: 3 additions & 0 deletions src/mpd_parser/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ class UnicodeDeclaredError(Exception):
""" Raised when the XML has an encoding declaration in it's manifest and the parser did not remove it """
description = "xml has encoding declaration, lxml cannot process it"

class UnknownValueError(Exception):
""" Raised when the XML parsing fails on unexpected issue, check error for more information """
description = "lxml failed to parse manifest, verify the input"

class UnknownElementTreeParseError(Exception):
""" Raised after a etree parse operation fails on an unexpected error """
Expand Down
15 changes: 14 additions & 1 deletion src/mpd_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
Main module of the package, Parser class
"""

import logging
from re import Match, sub
from urllib.request import urlopen

from lxml import etree

from mpd_parser.exceptions import UnicodeDeclaredError, UnknownElementTreeParseError
from mpd_parser.exceptions import UnicodeDeclaredError, UnknownElementTreeParseError, UnknownValueError
from mpd_parser.models.composite_tags import MPD

# module level logger, application will configure formatting and handlers
logger = logging.getLogger(__name__)

# Regular expression to match encoding declaration in XML
ENCODING_PATTERN = r"<\?.*?\s(encoding=\"\S*\").*\?>"


Expand Down Expand Up @@ -48,7 +52,10 @@ def cut_and_burn(match: Match) -> str:
except ValueError as err:
if "Unicode" in err.args[0]:
raise UnicodeDeclaredError() from err
logger.exception("Failed to parse manifest string")
raise UnknownValueError() from err
except Exception as err:
logger.exception("Failed to parse manifest string")
raise UnknownElementTreeParseError() from err
if encoding:
return MPD(root, encoding=encoding[0].groups()[0])
Expand All @@ -69,7 +76,10 @@ def from_file(cls, manifest_file_name: str) -> MPD:
except ValueError as err:
if "Unicode" in err.args[0]:
raise UnicodeDeclaredError() from err
logger.exception("Failed to parse manifest file %s", manifest_file_name)
raise UnknownValueError() from err
except Exception as err:
logger.exception("Failed to parse manifest file %s", manifest_file_name)
raise UnknownElementTreeParseError() from err
return MPD(tree.getroot())

Expand All @@ -89,7 +99,10 @@ def from_url(cls, url: str) -> MPD:
except ValueError as err:
if "Unicode" in err.args[0]:
raise UnicodeDeclaredError() from err
logger.exception("Failed to parse manifest from URL %s", url)
raise UnknownValueError() from err
except Exception as err:
logger.exception("Failed to parse manifest from URL %s", url)
raise UnknownElementTreeParseError() from err
return MPD(tree.getroot())

Expand Down
59 changes: 58 additions & 1 deletion tests/test_manifets.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
"""
Test the parsing of full manifests
"""
import io
import os

from pytest import mark
from pytest import mark, raises
from mpd_parser.exceptions import UnicodeDeclaredError, UnknownElementTreeParseError, UnknownValueError
from mpd_parser.parser import Parser

from tests.conftest import touch_attributes, MANIFESTS_DIR

class DummyFile(io.BytesIO):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()

def dummy_urlopen(url):
return DummyFile(b"<MPD></MPD>")


@mark.parametrize("input_file", [
"./../manifests/bigBuckBunny-onDemend.mpd",
Expand Down Expand Up @@ -104,3 +115,49 @@ def test_to_string(input_file):
0].range == \
transformed_mpd.periods[0].adaptation_sets[0].representations[0].segment_bases[0].initializations[
0].range

@mark.parametrize(
"exception,patch_func",
[
(UnicodeDeclaredError, lambda: ValueError("Unicode something")),
(UnknownValueError, lambda: ValueError("Some other value error")),
(UnknownElementTreeParseError, lambda: RuntimeError("Some runtime error")),
]
)
def test_from_string_error_handling(monkeypatch, exception, patch_func):
def fake_parse(*args, **kwargs):
raise patch_func()
monkeypatch.setattr("mpd_parser.parser.etree.fromstring", fake_parse)
with raises(exception):
Parser.from_string("<MPD></MPD>")

@mark.parametrize(
"exception,patch_func",
[
(UnicodeDeclaredError, lambda: ValueError("Unicode something")),
(UnknownValueError, lambda: ValueError("Some other value error")),
(UnknownElementTreeParseError, lambda: RuntimeError("Some runtime error")),
]
)
def test_from_file_error_handling(monkeypatch, exception, patch_func):
def fake_parse(*args, **kwargs):
raise patch_func()
monkeypatch.setattr("mpd_parser.parser.etree.parse", fake_parse)
with raises(exception):
Parser.from_file("dummy_file.mpd")

@mark.parametrize(
"exception,patch_func",
[
(UnicodeDeclaredError, lambda: ValueError("Unicode something")),
(UnknownValueError, lambda: ValueError("Some other value error")),
(UnknownElementTreeParseError, lambda: RuntimeError("Some runtime error")),
]
)
def test_from_url_error_handling(monkeypatch, exception, patch_func):
def fake_parse(*args, **kwargs):
raise patch_func()
monkeypatch.setattr("mpd_parser.parser.etree.parse", fake_parse)
monkeypatch.setattr("mpd_parser.parser.urlopen", dummy_urlopen)
with raises(exception):
Parser.from_url("http://dummy.url/manifest.mpd")