diff --git a/src/main/python/ttconv/imsc/reader.py b/src/main/python/ttconv/imsc/reader.py index 6258c049..12b5f499 100644 --- a/src/main/python/ttconv/imsc/reader.py +++ b/src/main/python/ttconv/imsc/reader.py @@ -25,8 +25,10 @@ '''IMSC reader''' +import io import logging import typing +import xml.etree.ElementTree as et import ttconv.imsc.elements as imsc_elements import ttconv.model as model @@ -34,10 +36,11 @@ LOGGER = logging.getLogger(__name__) -def to_model(xml_tree, progress_callback=lambda _: None) -> typing.Optional[model.ContentDocument]: +def to_model(data_file: typing.BinaryIO, _config=None, progress_callback=lambda _: None) -> typing.Optional[model.ContentDocument]: '''Convers an IMSC document to the data model''' - xml_element = xml_tree.getroot() + text_stream = io.TextIOWrapper(data_file, encoding='utf-8') + xml_element = et.parse(text_stream).getroot() if not imsc_elements.TTElement.is_instance(xml_element): LOGGER.fatal("A tt element is not the root element") diff --git a/src/main/python/ttconv/imsc/writer.py b/src/main/python/ttconv/imsc/writer.py index c22284af..092bf26f 100644 --- a/src/main/python/ttconv/imsc/writer.py +++ b/src/main/python/ttconv/imsc/writer.py @@ -43,13 +43,14 @@ def from_model( model_doc: model.ContentDocument, + output: typing.BinaryIO, config: typing.Optional[imsc_config.IMSCWriterConfiguration] = None, progress_callback: typing.Callable[[numbers.Real], typing.NoReturn] = lambda _: None ): '''Converts the data model to an IMSC document. The writer regularly the `progress_callback` function, if provided, with a real between 0 and 1, indicating the relative progress of the process. ''' - + et.register_namespace("", xml_ns.TTML) et.register_namespace("ttp", xml_ns.TTP) et.register_namespace("tts", xml_ns.TTS) @@ -78,7 +79,7 @@ def from_model( else: time_format = TimeExpressionSyntaxEnum.clock_time - return et.ElementTree( + et.ElementTree( imsc_elements.TTElement.from_model( model_doc, config.fps, @@ -86,4 +87,4 @@ def from_model( progress_callback, config.profile_signaling ) - ) + ).write(output, encoding="utf-8") diff --git a/src/main/python/ttconv/scc/reader.py b/src/main/python/ttconv/scc/reader.py index fa4fe855..32c96d5e 100644 --- a/src/main/python/ttconv/scc/reader.py +++ b/src/main/python/ttconv/scc/reader.py @@ -28,6 +28,7 @@ from __future__ import annotations import logging +import typing from typing import Optional from ttconv.model import ContentDocument, Body, Div, CellResolutionType, ActiveAreaType @@ -45,9 +46,11 @@ # SCC reader # -def to_model(scc_content: str, config: Optional[SccReaderConfiguration] = None, progress_callback=lambda _: None): +def to_model(data_file: typing.BinaryIO, config: Optional[SccReaderConfiguration] = None, progress_callback=lambda _: None): """Converts a SCC document to the data model""" + scc_content = data_file.read().decode("utf-8") + document = ContentDocument() # Safe area must be a 32x15 grid, that represents 80% of the root area @@ -104,8 +107,19 @@ def to_model(scc_content: str, config: Optional[SccReaderConfiguration] = None, return document -def to_disassembly(scc_content: str, show_channels = False) -> str: - """Dumps an SCC document into the disassembly format""" +def to_disassembly(scc_content: str | bytes, show_channels = False) -> str: + """Converts SCC content into a human-readable disassembly string. + + Args: + scc_content: SCC file content as a string or UTF-8 encoded bytes. + show_channels: If True, include the channel number for each data word. + + Returns: + A string with one disassembled line per SCC line, terminated by newlines. + """ + if isinstance(scc_content, bytes): + scc_content = scc_content.decode("utf-8") + disassembly = "" for line in scc_content.splitlines(): LOGGER.debug(line) diff --git a/src/main/python/ttconv/scc/writer.py b/src/main/python/ttconv/scc/writer.py index 55400f0e..c55ea618 100644 --- a/src/main/python/ttconv/scc/writer.py +++ b/src/main/python/ttconv/scc/writer.py @@ -30,6 +30,7 @@ import logging from fractions import Fraction import re +import typing from typing import List, Optional, Sequence import ttconv.model as model @@ -236,7 +237,7 @@ def _octet2hex(octet): # # scc writer # -def from_model(doc: model.ContentDocument, config: Optional[SccWriterConfiguration] = None, progress_callback=lambda _: None) -> str: +def from_model(doc: model.ContentDocument, output: typing.BinaryIO, config: Optional[SccWriterConfiguration] = None, progress_callback=lambda _: None): """Converts the data model to an SCC document""" # split progress between ISD construction and SCC writing @@ -420,4 +421,4 @@ def _isd_progress(progress: float): if start_offset + chunk.get_begin() < 0: raise RuntimeError("The SCC stream would start earlier than the specified start timecode") - return "Scenarist_SCC V1.0\n\n" + "\n\n".join(map(lambda e: e.to_string(config.frame_rate.fps, config.frame_rate.df, start_offset), chunks)) + output.write(b"Scenarist_SCC V1.0\n\n" + b"\n\n".join(map(lambda e: e.to_string(config.frame_rate.fps, config.frame_rate.df, start_offset).encode("utf-8"), chunks))) diff --git a/src/main/python/ttconv/srt/reader.py b/src/main/python/ttconv/srt/reader.py index 5b1b89eb..e398142b 100644 --- a/src/main/python/ttconv/srt/reader.py +++ b/src/main/python/ttconv/srt/reader.py @@ -28,6 +28,7 @@ from __future__ import annotations import typing +import io import re import logging from enum import Enum @@ -208,7 +209,7 @@ class _State(Enum): _DEFAULT_LINE_HEIGHT = styles.LengthType(125, styles.LengthType.Units.pct) _DEFAULT_SAFE_AREA_PCT = 10 -def to_model(data_file: typing.IO, _config: SRTReaderConfiguration = None, progress_callback=lambda _: None): +def to_model(data_file: typing.BinaryIO, _config: SRTReaderConfiguration = None, progress_callback=lambda _: None): """Converts an SRT document to the data model""" extended_tags = _config.extended_tags if isinstance(_config, SRTReaderConfiguration) else False @@ -235,7 +236,7 @@ def to_model(data_file: typing.IO, _config: SRTReaderConfiguration = None, progr body.push_child(div) - lines : str = data_file.readlines() + lines : str = io.TextIOWrapper(data_file, encoding='utf-8').readlines() state = _State.COUNTER current_p = None diff --git a/src/main/python/ttconv/srt/writer.py b/src/main/python/ttconv/srt/writer.py index fd43ac17..2395f8ae 100644 --- a/src/main/python/ttconv/srt/writer.py +++ b/src/main/python/ttconv/srt/writer.py @@ -26,6 +26,7 @@ """SRT writer""" import logging +import typing from fractions import Fraction from typing import List, Optional @@ -186,8 +187,8 @@ def __str__(self) -> str: # -def from_model(doc: model.ContentDocument, config: Optional[SRTWriterConfiguration] = None, progress_callback=lambda _: None) -> str: - """Converts the data model to a SRT document""" +def from_model(doc: model.ContentDocument, output: typing.BinaryIO, config: Optional[SRTWriterConfiguration] = None, progress_callback=lambda _: None): + """Converts the data model to a SRT document, writing the result to `output`.""" srt = SrtContext(config if config is not None else SRTWriterConfiguration()) @@ -217,4 +218,4 @@ def _isd_progress(progress: float): srt.finish() - return str(srt) + output.write(str(srt).encode("utf-8")) diff --git a/src/main/python/ttconv/stl/reader.py b/src/main/python/ttconv/stl/reader.py index 8715e34a..163c387a 100644 --- a/src/main/python/ttconv/stl/reader.py +++ b/src/main/python/ttconv/stl/reader.py @@ -40,7 +40,7 @@ # STL reader # -def to_model(data_file: typing.IO, config: typing.Optional[STLReaderConfiguration] = None, progress_callback=lambda _: None): +def to_model(data_file: typing.BinaryIO, config: typing.Optional[STLReaderConfiguration] = None, progress_callback=lambda _: None): """Converts an STL document to the data model""" m = DataFile( diff --git a/src/main/python/ttconv/tt.py b/src/main/python/ttconv/tt.py index 074f2b2c..d027b7cd 100755 --- a/src/main/python/ttconv/tt.py +++ b/src/main/python/ttconv/tt.py @@ -30,10 +30,8 @@ import os import sys import typing -import xml.etree.ElementTree as et from argparse import ArgumentParser from enum import Enum -from pathlib import Path from ttconv.filters.document_filter import DocumentFilter import ttconv.imsc.reader as imsc_reader @@ -300,28 +298,25 @@ def convert(args): writer_type = FileTypes.get_file_type(args.otype, output_file_extension) if reader_type is FileTypes.TTML: - # - # Parse the xml input file into an ElementTree # - tree = et.parse(inputfile) - - # - # Pass the parsed xml to the reader + # Open the file and pass it to the reader # - model = imsc_reader.to_model(tree, progress_callback_read) + reader_config = read_config_from_json(IMSCWriterConfiguration, json_config_data) - elif reader_type is FileTypes.SCC: - file_as_str = Path(inputfile).read_text() + with open(inputfile, "rb") as f: + model = imsc_reader.to_model(f, reader_config, progress_callback_read) + elif reader_type is FileTypes.SCC: # # Read the config # reader_config = read_config_from_json(SccReaderConfiguration, json_config_data) # - # Pass the parsed xml to the reader + # Open the file and pass it to the reader # - model = scc_reader.to_model(file_as_str, reader_config, progress_callback_read) + with open(inputfile, "rb") as f: + model = scc_reader.to_model(f, reader_config, progress_callback_read) elif reader_type is FileTypes.STL: # @@ -344,7 +339,7 @@ def convert(args): # # Open the file and pass it to the reader # - with open(inputfile, "r", encoding="utf-8") as f: + with open(inputfile, "rb") as f: model = srt_reader.to_model(f, reader_config, progress_callback_read) elif reader_type is FileTypes.VTT: @@ -352,7 +347,7 @@ def convert(args): # # Open the file and pass it to the reader # - with open(inputfile, "r", encoding="utf-8") as f: + with open(inputfile, "rb") as f: model = vtt_reader.to_model(f, None, progress_callback_read) else: @@ -401,12 +396,11 @@ def convert(args): # # Construct and configure the writer # - tree_from_model = imsc_writer.from_model(model, writer_config, progress_callback_write) - # # Write out the converted file # - tree_from_model.write(outputfile, encoding="utf-8") + with open(outputfile, "wb") as f: + imsc_writer.from_model(model, f, writer_config, progress_callback_write) elif writer_type is FileTypes.SRT: # @@ -417,13 +411,8 @@ def convert(args): # # Construct and configure the writer # - srt_document = srt_writer.from_model(model, writer_config, progress_callback_write) - - # - # Write out the converted file - # - with open(outputfile, "w", encoding="utf-8") as srt_file: - srt_file.write(srt_document) + with open(outputfile, "wb") as srt_file: + srt_writer.from_model(model, srt_file, writer_config, progress_callback_write) elif writer_type is FileTypes.VTT: # @@ -434,13 +423,11 @@ def convert(args): # # Construct and configure the writer # - vtt_document = vtt_writer.from_model(model, writer_config, progress_callback_write) - # # Write out the converted file # - with open(outputfile, "w", encoding="utf-8") as vtt_file: - vtt_file.write(vtt_document) + with open(outputfile, "wb") as vtt_file: + vtt_writer.from_model(model, vtt_file, writer_config, progress_callback_write) elif writer_type is FileTypes.SCC: # @@ -451,13 +438,11 @@ def convert(args): # # Construct and configure the writer # - scc_document = scc_writer.from_model(model, writer_config, progress_callback_write) - # # Write out the converted file # - with open(outputfile, "w", encoding="utf-8") as scc_file: - scc_file.write(scc_document) + with open(outputfile, "wb") as scc_file: + scc_writer.from_model(model, scc_file, writer_config, progress_callback_write) else: diff --git a/src/main/python/ttconv/vtt/reader.py b/src/main/python/ttconv/vtt/reader.py index 7682632e..a16ec845 100644 --- a/src/main/python/ttconv/vtt/reader.py +++ b/src/main/python/ttconv/vtt/reader.py @@ -28,6 +28,7 @@ from __future__ import annotations from dataclasses import dataclass +import io import typing import re import logging @@ -518,7 +519,7 @@ def vtt_timestamp_to_secs(vtt_ts: str): return None -def to_model(data_file: typing.IO, _config = None, progress_callback=lambda _: None): +def to_model(data_file: typing.BinaryIO, _config = None, progress_callback=lambda _: None): """Converts a WebVTT document to the data model""" class _State(Enum): @@ -539,7 +540,7 @@ class _State(Enum): div = model.Div(doc) body.push_child(div) - lines : str = data_file.readlines() + lines : str = io.TextIOWrapper(data_file, encoding='utf-8').readlines() state = _State.START current_p = None diff --git a/src/main/python/ttconv/vtt/writer.py b/src/main/python/ttconv/vtt/writer.py index 2a079e61..76f290c7 100644 --- a/src/main/python/ttconv/vtt/writer.py +++ b/src/main/python/ttconv/vtt/writer.py @@ -26,6 +26,7 @@ """WebVTT writer""" import logging +import typing from fractions import Fraction from typing import Dict, List, Optional @@ -269,7 +270,7 @@ def __str__(self) -> str: # -def from_model(doc: model.ContentDocument, config = None, progress_callback=lambda _: None) -> str: +def from_model(doc: model.ContentDocument, output: typing.BinaryIO, config=None, progress_callback=lambda _: None): """Converts the data model to a VTT document""" # split progress between ISD construction and VTT writing @@ -293,4 +294,4 @@ def _isd_progress(progress: float): vtt.finish() - return str(vtt) + output.write(str(vtt).encode("utf-8")) diff --git a/src/test/python/test_imsc11text_filter.py b/src/test/python/test_imsc11text_filter.py index d960fc93..fbf0a27a 100644 --- a/src/test/python/test_imsc11text_filter.py +++ b/src/test/python/test_imsc11text_filter.py @@ -35,8 +35,6 @@ import unittest import io from fractions import Fraction -import xml.etree.ElementTree as et - import ttconv.imsc.reader as imsc_reader import ttconv.model as model import ttconv.style_properties as styles @@ -98,7 +96,7 @@ def test_valid_document_passes(self): self.assertIn(IMSC_11_TEXT_PROFILE_DESIGNATOR, doc.get_content_profiles()) def test_valid_vtt_document_passes(self): - f = io.StringIO("WEBVTT\n\n00:00:00.000 --> 00:00:05.000\nHello world\n") + f = io.BytesIO(b"WEBVTT\n\n00:00:00.000 --> 00:00:05.000\nHello world\n") doc = to_model(f) filt = IMSC11TextFilter() filt.process(doc) @@ -610,8 +608,8 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name): logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - model = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + model = imsc_reader.to_model(f) self.assertIsNotNone(model) filt = IMSC11TextFilter() filt.process(model) @@ -624,8 +622,8 @@ def test_imsc_1_1_test_suite(self): if ext == ".ttml": with self.subTest(name): logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - model = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + model = imsc_reader.to_model(f) self.assertIsNotNone(model) filt = IMSC11TextFilter() filt.process(model) @@ -636,8 +634,8 @@ def test_imsc_1_3_test_suite(self): (name, ext) = os.path.splitext(filename) if ext == ".ttml": with self.subTest(name): - tree = et.parse(os.path.join(root, filename)) - model = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + model = imsc_reader.to_model(f) self.assertIsNotNone(model) filt = IMSC11TextFilter() with self.assertRaises(ValueError): diff --git a/src/test/python/test_imsc_reader.py b/src/test/python/test_imsc_reader.py index d131faa5..3b05df7f 100644 --- a/src/test/python/test_imsc_reader.py +++ b/src/test/python/test_imsc_reader.py @@ -27,6 +27,7 @@ # pylint: disable=R0201,C0115,C0116 +import io import unittest import xml.etree.ElementTree as et import os @@ -44,7 +45,7 @@ class IMSCReaderTest(unittest.TestCase): def test_reader_tt_element_not_root_element(self): - xml_str = """ + xml_str = b""" """ - tt_not_root = et.ElementTree(et.fromstring(xml_str)) - self.assertIsNone(imsc_reader.to_model(tt_not_root)) + self.assertIsNone(imsc_reader.to_model(io.BytesIO(xml_str))) def test_body_only(self): - tree = et.parse('src/test/resources/ttml/body_only.ttml') - imsc_reader.to_model(tree) + with open('src/test/resources/ttml/body_only.ttml', 'rb') as f: + imsc_reader.to_model(f) def test_basic_time_containment_001(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment001.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment001.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) body = doc.get_body() @@ -91,8 +91,8 @@ def test_basic_time_containment_001(self): self.assertEqual(span_children[1].get_end(), Fraction(10)) def test_basic_time_containment_002(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment002.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment002.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) body = doc.get_body() @@ -113,8 +113,8 @@ def test_basic_time_containment_002(self): self.assertEqual(p_children[1].get_end(), Fraction(20)) def test_basic_time_containment_003(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment003.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTimeContainment003.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) body = doc.get_body() @@ -143,8 +143,8 @@ def test_basic_time_containment_003(self): self.assertEqual(span_children[1].get_end(), Fraction(15)) def test_basic_timing_007(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming007.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming007.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) body = doc.get_body() @@ -175,8 +175,8 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - self.assertIsNotNone(imsc_reader.to_model(tree)) + with open(os.path.join(root, filename), 'rb') as f: + self.assertIsNotNone(imsc_reader.to_model(f)) if len(logs.output) > 1: self.fail(logs.output) @@ -186,8 +186,8 @@ def test_imsc_1_1_test_suite(self): (name, ext) = os.path.splitext(filename) if ext == ".ttml": with self.subTest(name): - tree = et.parse(os.path.join(root, filename)) - self.assertIsNotNone(imsc_reader.to_model(tree)) + with open(os.path.join(root, filename), 'rb') as f: + self.assertIsNotNone(imsc_reader.to_model(f)) def test_imsc_1_3_test_suite(self): for root, _subdirs, files in os.walk("src/test/resources/ttml/imsc-tests/imsc1_3/ttml"): @@ -195,12 +195,12 @@ def test_imsc_1_3_test_suite(self): (name, ext) = os.path.splitext(filename) if ext == ".ttml": with self.subTest(name): - tree = et.parse(os.path.join(root, filename)) - self.assertIsNotNone(imsc_reader.to_model(tree)) + with open(os.path.join(root, filename), 'rb') as f: + self.assertIsNotNone(imsc_reader.to_model(f)) def test_referential_styling(self): - tree = et.parse('src/test/resources/ttml/referential_styling.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/referential_styling.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) divs = list(doc.get_body()) @@ -219,15 +219,15 @@ def test_referential_styling(self): self.assertEqual(regions[1].get_style(styles.StyleProperties.BackgroundColor), styles.NamedColors.yellow.value) def test_initial(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1_1/ttml/initial/initial002.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1_1/ttml/initial/initial002.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) self.assertEqual(doc.get_initial_value(styles.StyleProperties.Color), styles.NamedColors.green.value) self.assertEqual(doc.get_initial_value(styles.StyleProperties.FontStyle), styles.FontStyleType.italic) def test_frame_rate(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/TimeExpressions001.ttml') - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/TimeExpressions001.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) #

24f = 1.001s

p = list(list(doc.get_body())[0])[3] @@ -235,7 +235,7 @@ def test_frame_rate(self): self.assertEqual(p.get_end(), Fraction(4394201, 1000)) def test_ooo_set_element(self): - xml_str = """ + xml_str = b""" @@ -246,11 +246,9 @@ def test_ooo_set_element(self): """ - tree = et.ElementTree(et.fromstring(xml_str)) - with self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - self.assertIsNotNone(imsc_reader.to_model(tree)) + self.assertIsNotNone(imsc_reader.to_model(io.BytesIO(xml_str))) if len(logs.output) != 2: self.fail(logs.output) @@ -272,24 +270,24 @@ def test_text_emphasis(self): self.assertEqual(value.position, styles.TextEmphasisType.Position.before) def test_cell_resolution(self): - xml_str = """ + xml_str = b""" """ - doc = imsc_reader.to_model(et.ElementTree(et.fromstring(xml_str))) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) self.assertEqual(doc.get_cell_resolution().columns, 32) self.assertEqual(doc.get_cell_resolution().rows, 15) def test_content_profiles(self): - xml_str = """ + xml_str = b""" """ - doc = imsc_reader.to_model(et.ElementTree(et.fromstring(xml_str))) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) self.assertSetEqual(doc.get_content_profiles(), {"http://www.w3.org/ns/ttml/profile/imsc1.1/text", "http://www.w3.org/ns/ttml/profile/imsc1/text"}) def test_timeBase_parameter(self): @@ -321,7 +319,7 @@ def test_dropframe_parameter(self): self.fail(logs.output) def test_smpte_tc_nondrop(self): - xml_str = """ + xml_str = b""" """ - doc = imsc_reader.to_model(et.ElementTree(et.fromstring(xml_str))) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) body = doc.get_body() self.assertEqual(body.get_begin(), (3723 * 30 + 20)/Fraction(30000, 1001)) def test_smpte_tc_drop(self): - xml_str = """ + xml_str = b""" """ - doc = imsc_reader.to_model(et.ElementTree(et.fromstring(xml_str))) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) body = doc.get_body() self.assertEqual(body.get_begin(), SmpteTimeCode(1, 2, 3, 20, Fraction(30000, 1001), True).to_temporal_offset()) diff --git a/src/test/python/test_imsc_writer.py b/src/test/python/test_imsc_writer.py index 684e5be6..bca65ef5 100644 --- a/src/test/python/test_imsc_writer.py +++ b/src/test/python/test_imsc_writer.py @@ -27,6 +27,7 @@ # pylint: disable=R0201,C0115,C0116 +import io import os import re import unittest @@ -72,9 +73,11 @@ def write_pretty_xml(self, tree: et.ElementTree, file_path): def test_default_ns(self): """Confirm that the tt ns is the default namespace""" file_to_parse = "src/test/resources/ttml/imsc-tests/imsc1/ttml/animation/Animation001.ttml" - tree = et.parse(file_to_parse) - test_model = imsc_reader.to_model(tree) - tree_from_model = imsc_writer.from_model(test_model) + with open(file_to_parse, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + imsc_writer.from_model(test_model, buf) + tree_from_model = et.ElementTree(et.fromstring(buf.getvalue())) rough_string = et.tostring(tree_from_model.getroot(), 'unicode') self.assertNotRegex(rough_string, "ttml:tt") @@ -83,14 +86,15 @@ def test_default_ns(self): def test_animation_001(self): file_to_parse = "src/test/resources/ttml/imsc-tests/imsc1/ttml/animation/Animation001.ttml" - - tree = et.parse(file_to_parse) # create the model - test_model = imsc_reader.to_model(tree) + with open(file_to_parse, 'rb') as f: + test_model = imsc_reader.to_model(f) # convert from a model to a ttml document - tree_from_model = imsc_writer.from_model(test_model) + buf = io.BytesIO() + imsc_writer.from_model(test_model, buf) + tree_from_model = et.ElementTree(et.fromstring(buf.getvalue())) # write the document out to a file self.write_pretty_xml(tree_from_model, 'build/out.ttml') @@ -105,9 +109,8 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - test_model = imsc_reader.to_model(tree) - tree_from_model = imsc_writer.from_model(test_model) + with open(os.path.join(root, filename), 'rb') as f: + test_model = imsc_reader.to_model(f) test_dir_relative_path = os.path.basename(root) @@ -116,7 +119,7 @@ def test_imsc_1_test_suite(self): os.makedirs(os.path.join(base_path, "ttml", test_dir_relative_path), exist_ok=True) with open(os.path.join(base_path, "ttml", test_relative_path), "wb") as f: - f.write(et.tostring(tree_from_model.getroot(), 'utf-8')) + imsc_writer.from_model(test_model, f) manifest.append({"path" : str(test_relative_path).replace('\\', '/')}) @@ -136,9 +139,8 @@ def test_imsc_1_1_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - test_model = imsc_reader.to_model(tree) - tree_from_model = imsc_writer.from_model(test_model) + with open(os.path.join(root, filename), 'rb') as f: + test_model = imsc_reader.to_model(f) test_dir_relative_path = os.path.basename(root) @@ -147,7 +149,7 @@ def test_imsc_1_1_test_suite(self): os.makedirs(os.path.join(base_path, "ttml", test_dir_relative_path), exist_ok=True) with open(os.path.join(base_path, "ttml", test_relative_path), "wb") as f: - f.write(et.tostring(tree_from_model.getroot(), 'utf-8')) + imsc_writer.from_model(test_model, f) manifest.append({"path" : str(test_relative_path).replace('\\', '/')}) @@ -167,9 +169,8 @@ def test_imsc_1_3_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - test_model = imsc_reader.to_model(tree) - tree_from_model = imsc_writer.from_model(test_model) + with open(os.path.join(root, filename), 'rb') as f: + test_model = imsc_reader.to_model(f) test_dir_relative_path = os.path.basename(root) @@ -178,7 +179,7 @@ def test_imsc_1_3_test_suite(self): os.makedirs(os.path.join(base_path, "ttml", test_dir_relative_path), exist_ok=True) with open(os.path.join(base_path, "ttml", test_relative_path), "wb") as f: - f.write(et.tostring(tree_from_model.getroot(), 'utf-8')) + imsc_writer.from_model(test_model, f) manifest.append({"path" : str(test_relative_path).replace('\\', '/')}) @@ -218,7 +219,8 @@ def test_body_only(self): doc.set_body(body) # write the document out to a file - imsc_writer.from_model(doc).write('build/BodyElement.out.ttml', encoding='utf-8', xml_declaration=True) + with open('build/BodyElement.out.ttml', 'wb') as f: + imsc_writer.from_model(doc, f) class StylePropertyWriterTest(unittest.TestCase): @@ -440,11 +442,13 @@ def test_tts_writing_no_extent_when_no_body(self): d = model.ContentDocument() - tree_from_model = imsc_writer.from_model(d) + buf = io.BytesIO() + imsc_writer.from_model(d, buf) + tree_from_model = et.ElementTree(et.fromstring(buf.getvalue())) extent = tree_from_model.getroot().attrib.get( f"{{{imsc_styles.StyleProperties.Extent.ns}}}{imsc_styles.StyleProperties.Extent.local_name}") - + self.assertEqual(extent, None) def test_tts_writing_no_extent_when_body_has_no_extents(self): @@ -463,11 +467,13 @@ def test_tts_writing_no_extent_when_body_has_no_extents(self): body.push_child(div) doc.set_body(body) - tree_from_model = imsc_writer.from_model(doc) + buf = io.BytesIO() + imsc_writer.from_model(doc, buf) + tree_from_model = et.ElementTree(et.fromstring(buf.getvalue())) extent = tree_from_model.getroot().attrib.get( f"{{{imsc_styles.StyleProperties.Extent.ns}}}{imsc_styles.StyleProperties.Extent.local_name}") - + self.assertEqual(extent, None) def test_tts_writing_extent_when_body_has_extents(self): @@ -496,11 +502,13 @@ def test_tts_writing_extent_when_body_has_extents(self): doc.put_region(r) - tree_from_model = imsc_writer.from_model(doc) + buf = io.BytesIO() + imsc_writer.from_model(doc, buf) + tree_from_model = et.ElementTree(et.fromstring(buf.getvalue())) extent = tree_from_model.getroot().attrib.get( f"{{{imsc_styles.StyleProperties.Extent.ns}}}{imsc_styles.StyleProperties.Extent.local_name}") - + self.assertEqual(extent, '1920px 1080px') def test_style_property_base_has_px(self): @@ -742,15 +750,21 @@ def test_profile_signaling(self): ttml_doc.set_content_profiles(source_cps) config = imsc_config.IMSCWriterConfiguration.parse({"profile_signaling" : "content_profiles"}) - xml_from_model = imsc_writer.from_model(ttml_doc, config) + buf = io.BytesIO() + imsc_writer.from_model(ttml_doc, buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) extracted_cps = {e for e in re.findall(r'\S+', xml_from_model.getroot().get(f"{{{xml_ns.TTP}}}contentProfiles"))} self.assertSetEqual(extracted_cps, source_cps) - xml_from_model = imsc_writer.from_model(ttml_doc, None) + buf = io.BytesIO() + imsc_writer.from_model(ttml_doc, buf, None) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) self.assertIsNone(xml_from_model.getroot().get(f"{{{xml_ns.TTP}}}contentProfiles")) config = imsc_config.IMSCWriterConfiguration.parse({"profile_signaling" : "none"}) - xml_from_model = imsc_writer.from_model(ttml_doc, config) + buf = io.BytesIO() + imsc_writer.from_model(ttml_doc, buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) self.assertIsNone(xml_from_model.getroot().get(f"{{{xml_ns.TTP}}}contentProfiles")) def test_clock_time(self): @@ -760,14 +774,16 @@ def test_clock_time(self): """ - ttml_doc = et.ElementTree(et.fromstring(ttml_doc_str)) - config = imsc_config.IMSCWriterConfiguration.parse({"time_format" : "clock_time"}) - xml_from_model = imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + buf = io.BytesIO() + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) body_element = xml_from_model.find("tt:body", {"tt": xml_ns.TTML}) self.assertEqual(body_element.get("begin"), "00:00:02.300") - xml_from_model = imsc_writer.from_model(imsc_reader.to_model(ttml_doc)) + buf = io.BytesIO() + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), buf) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) body_element = xml_from_model.find("tt:body", {"tt": xml_ns.TTML}) self.assertEqual(body_element.get("begin"), "00:00:02.300") @@ -779,21 +795,23 @@ def test_frames(self): """ - ttml_doc = et.ElementTree(et.fromstring(ttml_doc_str)) - config = imsc_config.IMSCWriterConfiguration.parse({"time_format": "frames", "fps": "30/1"}) - xml_from_model = imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + buf = io.BytesIO() + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) body_element = xml_from_model.find("tt:body", {"tt": xml_ns.TTML}) self.assertEqual(body_element.get("begin"), "150f") config = imsc_config.IMSCWriterConfiguration.parse({"fps": "30/1"}) - xml_from_model = imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + buf = io.BytesIO() + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) body_element = xml_from_model.find("tt:body", {"tt": xml_ns.TTML}) self.assertEqual(body_element.get("begin"), "150f") config = imsc_config.IMSCWriterConfiguration.parse({"time_format": "frames"}) with self.assertRaises(ValueError): - imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), io.BytesIO(), config) def test_clock_time_with_frames(self): ttml_doc_str = """ @@ -802,16 +820,16 @@ def test_clock_time_with_frames(self): """ - ttml_doc = et.ElementTree(et.fromstring(ttml_doc_str)) - config = imsc_config.IMSCWriterConfiguration.parse({"time_format": "clock_time_with_frames", "fps": "30/1"}) - xml_from_model = imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + buf = io.BytesIO() + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), buf, config) + xml_from_model = et.ElementTree(et.fromstring(buf.getvalue())) body_element = xml_from_model.find("tt:body", {"tt": xml_ns.TTML}) self.assertEqual(body_element.get("begin"), "00:00:02:03") config = imsc_config.IMSCWriterConfiguration.parse({"time_format": "clock_time_with_frames"}) with self.assertRaises(ValueError): - imsc_writer.from_model(imsc_reader.to_model(ttml_doc), config) + imsc_writer.from_model(imsc_reader.to_model(io.BytesIO(ttml_doc_str.encode())), io.BytesIO(), config) if __name__ == '__main__': unittest.main() diff --git a/src/test/python/test_isd.py b/src/test/python/test_isd.py index a2d83b36..4459b80d 100644 --- a/src/test/python/test_isd.py +++ b/src/test/python/test_isd.py @@ -28,11 +28,11 @@ # pylint: disable=R0201,C0115,C0116 import glob +import io import typing import unittest import os import logging -import xml.etree.ElementTree as et from fractions import Fraction import ttconv.imsc.reader as imsc_reader import ttconv.model as model @@ -229,8 +229,8 @@ def test_isd_10(self): class IMSCTestSuiteTest(unittest.TestCase): def test_display_none_handling(self): - xml_doc = et.parse("src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/MediaParTiming002.ttml") - doc = imsc_reader.to_model(xml_doc) + with open("src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/MediaParTiming002.ttml", 'rb') as f: + doc = imsc_reader.to_model(f) isd = ISD.from_model(doc, 0) regions = list(isd.iter_regions()) @@ -252,8 +252,8 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - m = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + m = imsc_reader.to_model(f) self.assertIsNotNone(m) sig_times = ISD.significant_times(m) for t in sig_times: @@ -269,8 +269,8 @@ def test_imsc_1_1_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - m = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + m = imsc_reader.to_model(f) self.assertIsNotNone(m) sig_times = ISD.significant_times(m) for t in sig_times: @@ -286,8 +286,8 @@ def test_imsc_1_3_test_suite(self): if ext == ".ttml": with self.subTest(name), self.assertLogs() as logs: logging.getLogger().info("*****dummy*****") # dummy log - tree = et.parse(os.path.join(root, filename)) - m = imsc_reader.to_model(tree) + with open(os.path.join(root, filename), 'rb') as f: + m = imsc_reader.to_model(f) self.assertIsNotNone(m) sig_times = ISD.significant_times(m) for t in sig_times: @@ -527,7 +527,7 @@ def test_text_decoration_inheritance(self): def test_textEmphasis_auto(self): """https://github.com/sandflow/ttconv/issues/400""" - xml_str = """ + xml_str = b""" @@ -542,8 +542,7 @@ def test_textEmphasis_auto(self): """ - tree = et.ElementTree(et.fromstring(xml_str)) - doc = imsc_reader.to_model(tree) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) isd = ISD.from_model(doc, 0) regions = list(isd.iter_regions()) @@ -557,7 +556,7 @@ def test_textEmphasis_auto(self): def test_direction_special_semantics(self): """https://github.com/sandflow/ttconv/issues/400""" - xml_str = """ + xml_str = b""" @@ -573,8 +572,7 @@ def test_direction_special_semantics(self): """ - tree = et.ElementTree(et.fromstring(xml_str)) - doc = imsc_reader.to_model(tree) + doc = imsc_reader.to_model(io.BytesIO(xml_str)) p1 = (ISD.from_model(doc, 0).get_region("rl"))[0][0][0][0] @@ -735,8 +733,8 @@ def walk(func: typing.Callable[[str, str], None]) -> None: @staticmethod def generate_reference_file(ttml_path) -> str: - tree = et.parse(ttml_path) - doc = imsc_reader.to_model(tree) + with open(ttml_path, 'rb') as f: + doc = imsc_reader.to_model(f) sig_times = ISD.significant_times(doc) output_lines = [] diff --git a/src/test/python/test_isd_cache.py b/src/test/python/test_isd_cache.py index f3d51720..05530bd5 100644 --- a/src/test/python/test_isd_cache.py +++ b/src/test/python/test_isd_cache.py @@ -27,12 +27,12 @@ # pylint: disable=R0201,C0115,C0116 +import io from fractions import Fraction import unittest from ttconv.isd import ISD import ttconv.model as model import ttconv.style_properties as styles -import xml.etree.ElementTree as et import ttconv.imsc.reader as imsc_reader @@ -107,7 +107,7 @@ def test_regions_with_many_p(self): def test_show_background(self): - ttml_doc = """ """ - doc = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc))) + doc = imsc_reader.to_model(io.BytesIO(ttml_doc)) sig_times = ISD.significant_times(doc) diff --git a/src/test/python/test_isd_lwsp.py b/src/test/python/test_isd_lwsp.py index 15e39e10..7767feaa 100644 --- a/src/test/python/test_isd_lwsp.py +++ b/src/test/python/test_isd_lwsp.py @@ -28,7 +28,6 @@ # pylint: disable=R0201,C0115,C0116 import unittest -import xml.etree.ElementTree as et import ttconv.imsc.reader as imsc_reader from ttconv.isd import ISD import ttconv.model as model @@ -36,9 +35,8 @@ class LSWPTests(unittest.TestCase): def test_lwsp_default(self): - tree = et.parse('src/test/resources/ttml/lwsp_default.ttml') - - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/lwsp_default.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) isd = ISD.from_model(doc, 0) @@ -69,9 +67,8 @@ def test_lwsp_default(self): self.assertEqual(spans[3][0].get_text(), "est") def test_lwsp_preserve(self): - tree = et.parse('src/test/resources/ttml/lwsp_preserve.ttml') - - doc = imsc_reader.to_model(tree) + with open('src/test/resources/ttml/lwsp_preserve.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) isd = ISD.from_model(doc, 0) diff --git a/src/test/python/test_scc_reader.py b/src/test/python/test_scc_reader.py index dd34285e..6d925859 100644 --- a/src/test/python/test_scc_reader.py +++ b/src/test/python/test_scc_reader.py @@ -26,6 +26,7 @@ """Unit tests for the SCC reader""" # pylint: disable=R0201,C0115,C0116,W0212 +import io import unittest from fractions import Fraction from numbers import Number @@ -100,7 +101,7 @@ def check_region_extent(self, elem: ContentElement, expected_cell_width: int, ex self.check_element_extent(elem, width, height, unit=LengthType.Units.pct) def test_scc_pop_on_content(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 01:02:53:14 94ae 94ae 9420 9420 947a 947a 97a2 97a2 a820 68ef f26e 2068 ef6e 6be9 6e67 2029 942c 942c 8080 8080 942f 942f @@ -135,7 +136,7 @@ def test_scc_pop_on_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -213,7 +214,7 @@ def test_scc_pop_on_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_scc_pop_on_content_unexpectedly_ended(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:02:16 942c @@ -231,7 +232,7 @@ def test_scc_pop_on_content_unexpectedly_ended(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -257,7 +258,7 @@ def test_scc_pop_on_content_unexpectedly_ended(self): self.assertEqual(region_1, p_list[0].get_region()) def test_scc_pop_on_content_without_preamble_address_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:02:16 942c @@ -281,7 +282,7 @@ def test_scc_pop_on_content_without_preamble_address_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -313,7 +314,7 @@ def test_scc_pop_on_content_without_preamble_address_code(self): self.assertEqual(region_1, p_list[0].get_region()) def test_scc_double_word_in_content(self): - scc_content = """"Scenarist_SCC V1.0 + scc_content = b""""Scenarist_SCC V1.0 01:02:53:14 9420 9420 94AE 94AE 9452 9452 97A1 97A1 20F2 E56D E56D 62E5 F220 9137 9137 9137 9137 942F 942F 01:02:55:14 942c 942c """ @@ -322,7 +323,7 @@ def test_scc_double_word_in_content(self): """ self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) body = doc.get_body() self.assertIsNotNone(body) @@ -341,7 +342,7 @@ def test_scc_double_word_in_content(self): self.assertEqual(" remember ♪♪", first_text) def test_2_rows_roll_up_content(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:00:22 9425 9425 94ad 94ad 9470 9470 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 @@ -363,7 +364,7 @@ def test_2_rows_roll_up_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -409,7 +410,7 @@ def test_2_rows_roll_up_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_3_rows_roll_up_content(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:17;01 9426 9426 94ad 94ad 9470 9470 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 @@ -430,7 +431,7 @@ def test_3_rows_roll_up_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -472,7 +473,7 @@ def test_3_rows_roll_up_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_4_rows_roll_up_content(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:34;27 94a7 94ad 9470 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 @@ -499,7 +500,7 @@ def test_4_rows_roll_up_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -549,7 +550,7 @@ def test_4_rows_roll_up_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_mix_rows_roll_up_content(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:00;22 9425 9425 94ad 94ad 9470 9470 3e3e 3e20 c849 ae80 @@ -607,7 +608,7 @@ def test_mix_rows_roll_up_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -702,7 +703,7 @@ def test_mix_rows_roll_up_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_scc_roll_up_content_without_preamble_address_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:34:27 9425 94ad 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 @@ -721,7 +722,7 @@ def test_scc_roll_up_content_without_preamble_address_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -759,7 +760,7 @@ def test_scc_roll_up_content_without_preamble_address_code(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_scc_paint_on_content(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:02:53:14 9429 9429 94d2 94d2 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 94f2 94f2 636f 6e73 6563 7465 7475 7220 6164 6970 6973 6369 6e67 2065 6c69 742e @@ -778,7 +779,7 @@ def test_scc_paint_on_content(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("paint1") @@ -859,7 +860,7 @@ def test_scc_paint_on_content(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_scc_paint_on_content_without_preamble_address_codes(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:02:53:14 9429 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 @@ -878,7 +879,7 @@ def test_scc_paint_on_content_without_preamble_address_codes(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("paint1") @@ -936,7 +937,7 @@ def test_scc_paint_on_content_without_preamble_address_codes(self): self.check_element_style(span, StyleProperties.BackgroundColor, NamedColors.black.value) def test_scc_mid_row_erase_displayed_memory_control_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:00:00 9420 9150 4c6f 7265 6d20 6970 7375 6d20 646f 6c6f 7220 7369 7420 616d 6574 2c80 942c 8080 8080 942f @@ -958,7 +959,7 @@ def test_scc_mid_row_erase_displayed_memory_control_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1019,7 +1020,7 @@ def test_scc_mid_row_erase_displayed_memory_control_code(self): self.assertEqual(region_4, p_list[5].get_region()) def test_scc_content_starting_with_text(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:03:01 6970 7375 6d00 942c 942f @@ -1037,7 +1038,7 @@ def test_scc_content_starting_with_text(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1073,7 +1074,7 @@ def test_scc_content_starting_with_text(self): self.assertEqual(region_2, p_list[1].get_region()) def test_scc_content_starting_with_mid_row_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:03:01 91ae 6970 7375 6d00 942c 942f @@ -1091,7 +1092,7 @@ def test_scc_content_starting_with_mid_row_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1127,7 +1128,7 @@ def test_scc_content_starting_with_mid_row_code(self): self.assertEqual(region_2, p_list[1].get_region()) def test_scc_content_starting_with_control_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:03:01 942c 6970 7375 6d00 942c 942f @@ -1145,7 +1146,7 @@ def test_scc_content_starting_with_control_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1181,7 +1182,7 @@ def test_scc_content_starting_with_control_code(self): self.assertEqual(region_2, p_list[1].get_region()) def test_scc_content_starting_with_preamble_address_code(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:03:01 9370 6970 7375 6d00 942c 942f @@ -1199,7 +1200,7 @@ def test_scc_content_starting_with_preamble_address_code(self): self.assertEqual(scc_disassembly, to_disassembly(scc_content)) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1235,7 +1236,7 @@ def test_scc_content_starting_with_preamble_address_code(self): self.assertEqual(region_2, p_list[1].get_region()) def test_scc_with_negative_cursor(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 00:00:01:00 94AE 94AE 9420 9420 94F8 94F8 45E5 E5E3 68A1 94F4 94F4 D3E3 61F2 79A1 942C 942C 942F 942F 00:00:02:00 942F 942F """ @@ -1246,7 +1247,7 @@ def test_scc_with_negative_cursor(self): scc_disassembly = to_disassembly(scc_content) self.assertEqual(scc_disassembly_expected, scc_disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) @@ -1272,20 +1273,20 @@ def test_scc_with_negative_cursor(self): self.assertEqual(region_1, p_list[0].get_region()) def test_scc_content_starting_with_tab(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 00:42:41;20 942C 9429 97A2 5BF7 """ print(to_disassembly(scc_content)) - self.assertIsNotNone(to_model(scc_content)) + self.assertIsNotNone(to_model(io.BytesIO(scc_content))) def test_scc_content_starting_with_bs(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 00:42:41;20 942C 9429 94A1 5BF7 """ - self.assertIsNotNone(to_model(scc_content)) + self.assertIsNotNone(to_model(io.BytesIO(scc_content))) def test_scc_content_starting_with_backspace(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 10:01:44;17 94AE 9420 9470 9723 946E 94A1 92B0 20ec 6120 e6e9 6e20 64e5 7320 616e 6edc e573 2031 38b0 b02c 942C 8080 8080 942F """ @@ -1296,7 +1297,7 @@ def test_scc_content_starting_with_backspace(self): scc_disassembly = to_disassembly(scc_content) self.assertEqual(expected_scc_disassembly, scc_disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("pop1") @@ -1321,7 +1322,7 @@ def test_scc_content_starting_with_backspace(self): self.assertEqual(region_1, p_list[0].get_region()) def test_scc_content_roll_up_empty_caption(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 10:03:20:16 94ad 94ad 9426 9426 92d0 92d0 a880 9138 9138 2064 942c 942c e575 f820 76ef e9f8 2c20 e56e 2061 6e67 ec61 e973 29ba """ expected_scc_disassembly = """\ @@ -1331,7 +1332,7 @@ def test_scc_content_roll_up_empty_caption(self): scc_disassembly = to_disassembly(scc_content) self.assertEqual(expected_scc_disassembly, scc_disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) region_1 = doc.get_region("rollup1") @@ -1366,7 +1367,7 @@ def test_scc_content_roll_up_empty_caption(self): self.assertEqual(region_2, p_list[1].get_region()) def test_scc_text_without_style_nor_position(self): - scc_content = """Scenarist_SCC V1.0 + scc_content = b"""Scenarist_SCC V1.0 10:55:31:29 2080 3280 2046 3180 """ expected_scc_disassembly = """\ @@ -1376,7 +1377,7 @@ def test_scc_text_without_style_nor_position(self): scc_disassembly = to_disassembly(scc_content) self.assertEqual(expected_scc_disassembly, scc_disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) body = doc.get_body() @@ -1389,7 +1390,7 @@ def test_scc_text_without_style_nor_position(self): self.assertEqual(0, len(list(div))) def test_scc_content_with_paragraph_of_spaces(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 00:00:03:01 9370 6970 7375 6d00 942c 942f @@ -1408,7 +1409,7 @@ def test_scc_content_with_paragraph_of_spaces(self): disassembly = to_disassembly(scc_content) self.assertEqual(expected_disassembly, disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) @@ -1455,7 +1456,7 @@ def test_scc_content_with_paragraph_of_spaces(self): self.assertEqual(region_3, p_list[2].get_region()) def test_scc_content_trying_to_roll_up_pop_on_paragraph(self): - scc_content = """\ + scc_content = b"""\ Scenarist_SCC V1.0 11:19:24:05 9420 946E A861 7070 EC61 7564 E973 73E5 6DE5 6EF4 7329 9420 942C 942F @@ -1470,7 +1471,7 @@ def test_scc_content_trying_to_roll_up_pop_on_paragraph(self): disassembly = to_disassembly(scc_content) self.assertEqual(expected_disassembly, disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) @@ -1506,7 +1507,7 @@ def test_scc_content_trying_to_roll_up_pop_on_paragraph(self): self.assertEqual(region_2, p_list[1].get_region()) def test_skipping_channel_2_content(self): - scc_content = """\ + scc_content = b"""\ 01:03:27:29 1c20 1cd0 a843 4332 2920 1c2c 94ae 94ae 9420 9420 94f2 94f2 c845 d92c 2054 c845 5245 ae80 942c 942c 8080 8080 942f 942f """ expected_disassembly = """\ @@ -1516,7 +1517,7 @@ def test_skipping_channel_2_content(self): disassembly = to_disassembly(scc_content, show_channels=True) self.assertEqual(expected_disassembly, disassembly) - doc = to_model(scc_content) + doc = to_model(io.BytesIO(scc_content)) self.assertIsNotNone(doc) diff --git a/src/test/python/test_scc_writer.py b/src/test/python/test_scc_writer.py index 6c1cbbbd..759f60b8 100644 --- a/src/test/python/test_scc_writer.py +++ b/src/test/python/test_scc_writer.py @@ -27,6 +27,7 @@ # pylint: disable=R0201,C0115,C0116,W0212 +import io import json import os import unittest @@ -100,7 +101,7 @@ def test_frame_rate(self): class SCCWriterTest(unittest.TestCase): def test_basic(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -112,7 +113,7 @@ def test_basic(self): """ - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00;21 9420 9420 94ae 94ae 9440 9440 c8e5 ecec ef80 942f 942f @@ -123,7 +124,7 @@ def test_basic(self): 00:00:05;00 942c 942c""" def test_pop_on_centered(self): - ttml_doc_str = """ + ttml_doc_str = b""" """ - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00;21 9420 9420 94ae 94ae 94d6 94d6 20c8 e5ec ecef 942f 942f 00:00:03;00 942c 942c""" - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) assert model is not None config = SccWriterConfiguration() - scc_from_model = scc_writer.from_model(model, config) + buf = io.BytesIO() + scc_writer.from_model(model, buf, config) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) def test_pop_on_right_aligned(self): - ttml_doc_str = """ + ttml_doc_str = b""" """ - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00;20 9420 9420 94ae 94ae 94dc 94dc 2020 20c8 e5ec ecef 942f 942f 00:00:03;00 942c 942c""" - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) assert model is not None config = SccWriterConfiguration() - scc_from_model = scc_writer.from_model(model, config) + buf = io.BytesIO() + scc_writer.from_model(model, buf, config) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) def test_rollup(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -186,7 +191,7 @@ def test_rollup(self): """ - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00;28 94a7 94a7 94ad 94ad 9470 9470 c8e5 ecec ef80 @@ -196,13 +201,15 @@ def test_rollup(self): 00:00:10;00 942c 942c""" - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) config = SccWriterConfiguration() - scc_from_model = scc_writer.from_model(model, config) + buf = io.BytesIO() + scc_writer.from_model(model, buf, config) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) # round-trip test - rt_model = scc_reader.to_model(scc_from_model) + rt_model = scc_reader.to_model(io.BytesIO(scc_from_model)) # cfg = IMSCWriterConfiguration(time_format=TimeExpressionSyntaxEnum.frames, fps=Fraction(30000, 1001)) # imsc_writer.from_model(rt_model, cfg).write(sys.stdout.buffer) b = rt_model.get_body() @@ -214,7 +221,7 @@ def test_rollup(self): self.assertEqual(Fraction(300 * 1001, 30000), p1.get_end()) def test_basic_2997NDF(self): - ttml_doc_str = """ + ttml_doc_str = b"""
@@ -223,21 +230,23 @@ def test_basic_2997NDF(self): """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) assert model is not None - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:59:56:03 9420 9420 94ae 94ae 9440 9440 c8e5 ecec ef80 942f 942f 00:59:58:12 942c 942c""" config = SccWriterConfiguration(frame_rate=SCCFrameRate.FPS_2997_NDF) - scc_from_model = scc_writer.from_model(model, config) + buf = io.BytesIO() + scc_writer.from_model(model, buf, config) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) def test_basic_30FPS(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -249,10 +258,10 @@ def test_basic_30FPS(self): """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) assert model is not None - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00:21 9420 9420 94ae 94ae 9440 9440 c8e5 ecec ef80 942f 942f @@ -263,11 +272,13 @@ def test_basic_30FPS(self): 00:00:05:00 942c 942c""" config = SccWriterConfiguration(frame_rate=SCCFrameRate.FPS_30_NDF) - scc_from_model = scc_writer.from_model(model, config) + buf = io.BytesIO() + scc_writer.from_model(model, buf, config) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) def test_zero_start(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -278,21 +289,25 @@ def test_zero_start(self): """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) with self.assertRaises(RuntimeError): - scc_writer.from_model(model) + scc_writer.from_model(model, io.BytesIO()) - scc_from_model = scc_writer.from_model(model, SccWriterConfiguration(start_tc="01:00:00;00")) - expected_scc="""Scenarist_SCC V1.0 + buf = io.BytesIO() + scc_writer.from_model(model, buf, SccWriterConfiguration(start_tc="01:00:00;00")) + scc_from_model = buf.getvalue() + expected_scc=b"""Scenarist_SCC V1.0 00:59:59;21 9420 9420 94ae 94ae 9440 9440 c8e5 ecec ef80 942f 942f 01:00:02;29 942c 942c""" self.assertEqual(scc_from_model, expected_scc) - scc_from_model = scc_writer.from_model(model, SccWriterConfiguration(frame_rate=SCCFrameRate.FPS_30_NDF, start_tc="01:00:00:00")) - expected_scc="""Scenarist_SCC V1.0 + buf = io.BytesIO() + scc_writer.from_model(model, buf, SccWriterConfiguration(frame_rate=SCCFrameRate.FPS_30_NDF, start_tc="01:00:00:00")) + scc_from_model = buf.getvalue() + expected_scc=b"""Scenarist_SCC V1.0 00:59:59:21 9420 9420 94ae 94ae 9440 9440 c8e5 ecec ef80 942f 942f @@ -300,7 +315,7 @@ def test_zero_start(self): self.assertEqual(scc_from_model, expected_scc) def test_multi_regions(self): - expected_scc="""Scenarist_SCC V1.0 + expected_scc=b"""Scenarist_SCC V1.0 00:00:00;22 9420 9420 94ae 94ae 9440 9440 6180 942f 942f @@ -310,7 +325,7 @@ def test_multi_regions(self): 00:00:03;29 942c 942c""" - SAMPLE = """ + SAMPLE = b""" @@ -327,8 +342,10 @@ def test_multi_regions(self): """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(SAMPLE))) - scc_from_model = scc_writer.from_model(model) + model = imsc_reader.to_model(io.BytesIO(SAMPLE)) + buf = io.BytesIO() + scc_writer.from_model(model, buf) + scc_from_model = buf.getvalue() self.assertEqual(scc_from_model, expected_scc) if __name__ == '__main__': diff --git a/src/test/python/test_srt_reader.py b/src/test/python/test_srt_reader.py index e1c3f9df..5720b95c 100644 --- a/src/test/python/test_srt_reader.py +++ b/src/test/python/test_srt_reader.py @@ -40,7 +40,7 @@ class SrtReaderTest(unittest.TestCase): def test_sample(self): # from https://en.wikipedia.org/wiki/SubRip - SAMPLE = """1 + SAMPLE = b"""1 00:02:16,612 --> 00:02:19,376 Senator, we're making our final approach into Coruscant. @@ -61,12 +61,12 @@ def test_sample(self): 00:03:20,476 --> 00:03:22,671 There was no danger at all.""" - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) def test_bold(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob """) @@ -79,7 +79,7 @@ def test_bold(self): def test_blank_lines(self): # from https://en.wikipedia.org/wiki/SubRip - SAMPLE = """ + SAMPLE = b""" 1 00:02:16,612 --> 00:02:19,376 @@ -99,60 +99,60 @@ def test_blank_lines(self): """ - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) def test_bold_alt(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {bold}my{/bold} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: self.fail() - + def test_bold_alt2(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: self.fail() def test_bold_alt3(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {b}my{/b} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontWeight) == styles.FontWeightType.bold: self.fail() def test_italic(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob """) @@ -164,55 +164,55 @@ def test_italic(self): self.fail() def test_italic_alt(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {italic}my{/italic} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: self.fail() def test_italic_alt1(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {i}my{/i} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: self.fail() def test_italic_alt2(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): if e.get_style(styles.StyleProperties.FontStyle) == styles.FontStyleType.italic: self.fail() def test_underline(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob """) @@ -225,61 +225,61 @@ def test_underline(self): self.fail() def test_underline_alt(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {underline}my{/underline} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: self.fail() def test_underline_alt1(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello {u}my{/u} name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: self.fail() def test_underline_alt2(self): - f = io.StringIO(r"""1 + srt_data = b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob -""") - doc = to_model(f, SRTReaderConfiguration(extended_tags=True)) +""" + doc = to_model(io.BytesIO(srt_data), SRTReaderConfiguration(extended_tags=True)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: break else: self.fail() - doc = to_model(f) + doc = to_model(io.BytesIO(srt_data)) for e in doc.get_body().dfs_iterator(): text_decoration = e.get_style(styles.StyleProperties.TextDecoration) if text_decoration is not None and text_decoration.underline: self.fail() def test_blue(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob """) @@ -292,7 +292,7 @@ def test_blue(self): self.fail() def test_multiline_tags(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:02:16,612 --> 00:02:19,376 Hello my name is Bob @@ -305,7 +305,7 @@ def test_multiline_tags(self): self.fail() def test_long_hours(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 101:00:00,000 --> 101:00:01,000 Hello my name is Bob """) @@ -322,7 +322,7 @@ def test_long_hours(self): ) def test_single_line_text(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 101:00:00,000 --> 101:00:01,000 Hello """) @@ -334,7 +334,7 @@ def test_single_line_text(self): self.assertEqual(p_children[0].first_child().get_text(), "Hello") def test_multiline_text(self): - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 101:00:00,000 --> 101:00:01,000 Hello World @@ -351,7 +351,7 @@ def test_multiline_text(self): def test_alignment_tags_disabled_by_default(self): """Alignment tags should NOT be parsed when alignment_tags=False (default)""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 {\an1} Bottom Left """) @@ -370,7 +370,7 @@ def test_alignment_tags_disabled_by_default(self): def test_alignment_tags_all_positions(self): """Test all 9 alignment positions with alignment_tags=True""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 {\an1} V: Bottom - H: Left @@ -407,11 +407,11 @@ def test_alignment_tags_all_positions(self): {\an9} V: Top - H: Right """) doc = to_model(f, SRTReaderConfiguration(alignment_tags=True)) - + # Should have 9 alignment regions (r_an2 is shared as default) regions = list(doc.iter_regions()) self.assertEqual(len(regions), 9) # r_an1 through r_an9 - + # Check alignment regions exist with correct properties expected_alignments = { 1: (styles.DisplayAlignType.after, styles.TextAlignType.start), @@ -424,7 +424,7 @@ def test_alignment_tags_all_positions(self): 8: (styles.DisplayAlignType.before, styles.TextAlignType.center), 9: (styles.DisplayAlignType.before, styles.TextAlignType.end), } - + for code, (display_align, text_align) in expected_alignments.items(): region = doc.get_region(f"r_an{code}") self.assertIsNotNone(region, f"Region r_an{code} should exist") @@ -441,7 +441,7 @@ def test_alignment_tags_all_positions(self): def test_alignment_tag_stripped_from_text(self): """Alignment tag should be removed from displayed text""" - f = io.StringIO(r"""1 + f = io.BytesIO(b"""1 00:00:00,000 --> 00:00:01,000 {\an7} Top Left Text """) @@ -456,7 +456,7 @@ def test_alignment_tag_stripped_from_text(self): def test_alignment_region_safe_area(self): """Alignment regions should use fixed 10% safe area margin""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 {\an1} Test """) @@ -464,7 +464,7 @@ def test_alignment_region_safe_area(self): region = doc.get_region("r_an1") origin = region.get_style(styles.StyleProperties.Origin) extent = region.get_style(styles.StyleProperties.Extent) - + # Fixed 10% safe area: origin at (10%, 10%), extent is (80%, 80%) self.assertEqual(origin.x.value, 10) self.assertEqual(origin.y.value, 10) @@ -473,7 +473,7 @@ def test_alignment_region_safe_area(self): def test_alignment_mixed_paragraphs(self): """Mix of paragraphs with and without alignment tags""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 No alignment tag here @@ -486,20 +486,20 @@ def test_alignment_mixed_paragraphs(self): Also no alignment tag """) doc = to_model(f, SRTReaderConfiguration(alignment_tags=True)) - + paragraphs = list(doc.get_body().first_child()) self.assertEqual(len(paragraphs), 3) - + # First and third paragraphs should not have a region set (use default) self.assertIsNone(paragraphs[0].get_region()) self.assertIsNone(paragraphs[2].get_region()) - + # Second paragraph should have r_an7 region self.assertEqual(paragraphs[1].get_region().get_id(), "r_an7") def test_alignment_region_reuse(self): """Multiple paragraphs with same alignment should share region""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 {\an1} First bottom left @@ -512,32 +512,32 @@ def test_alignment_region_reuse(self): {\an9} Top right """) doc = to_model(f, SRTReaderConfiguration(alignment_tags=True)) - + paragraphs = list(doc.get_body().first_child()) - + # First two paragraphs should share the same region self.assertIs(paragraphs[0].get_region(), paragraphs[1].get_region()) self.assertEqual(paragraphs[0].get_region().get_id(), "r_an1") - + # Third paragraph should have different region self.assertEqual(paragraphs[2].get_region().get_id(), "r_an9") - + # Total alignment regions should be 2 (r_an1 and r_an9) + default r_an2 regions = list(doc.iter_regions()) self.assertEqual(len(regions), 3) def test_alignment_multiple_tags_uses_first(self): """Multiple alignment tags in same caption: uses first, removes all""" - f = io.StringIO(r"""1 + f = io.BytesIO(rb"""1 00:00:00,000 --> 00:00:01,000 {\an1}{\an9} Multiple tags here """) doc = to_model(f, SRTReaderConfiguration(alignment_tags=True)) p = doc.get_body().first_child().first_child() - + # Should use first alignment (an1 = bottom-left) self.assertEqual(p.get_region().get_id(), "r_an1") - + # Both tags should be removed from text text_content = "" for e in p.dfs_iterator(): diff --git a/src/test/python/test_srt_reader_writer.py b/src/test/python/test_srt_reader_writer.py index 47eb3fad..6223649f 100644 --- a/src/test/python/test_srt_reader_writer.py +++ b/src/test/python/test_srt_reader_writer.py @@ -29,8 +29,6 @@ import unittest import io import os -import xml.etree.ElementTree as et - import ttconv.srt.reader as srt_reader import ttconv.srt.writer as srt_writer import ttconv.imsc.reader as imsc_reader @@ -44,10 +42,11 @@ def test_imsc_1_test_suite(self): (name, ext) = os.path.splitext(filename) if ext == ".ttml": with self.subTest(name): - tree = et.parse(os.path.join(root, filename)) - doc = imsc_reader.to_model(tree) - srt_file = srt_writer.from_model(doc) - srt_reader.to_model(io.StringIO(srt_file)) + with open(os.path.join(root, filename), 'rb') as f: + doc = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(doc, buf) + srt_reader.to_model(io.BytesIO(buf.getvalue())) if __name__ == '__main__': unittest.main() diff --git a/src/test/python/test_srt_writer.py b/src/test/python/test_srt_writer.py index ba836ba5..aa1b1f0f 100644 --- a/src/test/python/test_srt_writer.py +++ b/src/test/python/test_srt_writer.py @@ -27,12 +27,10 @@ # pylint: disable=R0201,C0115,C0116,W0212 +import io import os import unittest -import xml.etree.ElementTree as et from fractions import Fraction -from pathlib import Path - import ttconv.imsc.reader as imsc_reader import ttconv.scc.reader as scc_reader import ttconv.srt.writer as srt_writer @@ -87,7 +85,7 @@ def test_srt_writer(self): span.push_child(Text(doc, "Pellentesque interdum lacinia sollicitudin.")) p.push_child(span) - expected_srt = """1 + expected_srt = b"""1 00:00:00,000 --> 00:00:02,000 Lorem ipsum dolor sit amet, @@ -100,7 +98,9 @@ def test_srt_writer(self): Pellentesque interdum lacinia sollicitudin. """ - srt_from_model = srt_writer.from_model(doc) + buf = io.BytesIO() + srt_writer.from_model(doc, buf) + srt_from_model = buf.getvalue() self.assertEqual(expected_srt, srt_from_model) @@ -111,9 +111,11 @@ def test_scc_test_suite(self): if ext == ".scc": with self.subTest(name): path = os.path.join(root, filename) - scc_content = Path(path).read_text() - test_model = scc_reader.to_model(scc_content) - srt_from_model = srt_writer.from_model(test_model) + with open(path, 'rb') as f: + test_model = scc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(test_model, buf) + srt_from_model = buf.getvalue() self.assertTrue(len(srt_from_model) > 0, msg=f"Could not convert {path}") self._check_output_srt(test_model, srt_from_model, path) @@ -124,10 +126,11 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - srt_from_model = srt_writer.from_model(test_model) - self._check_output_srt(test_model, srt_from_model, path) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(test_model, buf) + self._check_output_srt(test_model, buf.getvalue(), path) @unittest.skip("Too long to process") def test_imsc_1_1_test_suite(self): @@ -137,10 +140,11 @@ def test_imsc_1_1_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - srt_from_model = srt_writer.from_model(test_model) - self._check_output_srt(test_model, srt_from_model, path) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(test_model, buf) + self._check_output_srt(test_model, buf.getvalue(), path) @unittest.skip("IMSC 1.2 is not supported") def test_imsc_1_2_test_suite(self): @@ -150,10 +154,11 @@ def test_imsc_1_2_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - srt_from_model = srt_writer.from_model(test_model) - self._check_output_srt(test_model, srt_from_model, path) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(test_model, buf) + self._check_output_srt(test_model, buf.getvalue(), path) @unittest.skip("IMSC 1.3 is not supported") def test_imsc_1_3_test_suite(self): @@ -163,10 +168,11 @@ def test_imsc_1_3_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - srt_from_model = srt_writer.from_model(test_model) - self._check_output_srt(test_model, srt_from_model, path) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(test_model, buf) + self._check_output_srt(test_model, buf.getvalue(), path) # @@ -209,11 +215,13 @@ def _check_output_srt(self, model: ContentDocument, srt: str, path: str): self.assertEqual(0, len(srt), msg=f"Could not convert {path}") def test_empty_isds(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming010.ttml') - doc = imsc_reader.to_model(tree) - srt_from_model = srt_writer.from_model(doc) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming010.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) + buf = io.BytesIO() + srt_writer.from_model(doc, buf) + srt_from_model = buf.getvalue() - self.assertEqual(srt_from_model, """1 + self.assertEqual(srt_from_model, b"""1 00:00:10,000 --> 00:00:24,400 This text must appear at 10 seconds and disappear at 24.4 seconds @@ -223,7 +231,7 @@ def test_empty_isds(self): """) def test_text_formatting_disabled(self): - ttml_doc_str = """ + ttml_doc_str = b"""
@@ -232,20 +240,21 @@ def test_text_formatting_disabled(self): """ - ttml_doc = et.ElementTree(et.fromstring(ttml_doc_str)) - doc = imsc_reader.to_model(ttml_doc) + doc = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) config = srt_config.SRTWriterConfiguration.parse({"text_formatting": False}) - srt_from_model = srt_writer.from_model(doc, config) + buf = io.BytesIO() + srt_writer.from_model(doc, buf, config) + srt_from_model = buf.getvalue() - self.assertEqual(srt_from_model, """1 + self.assertEqual(srt_from_model, b"""1 00:00:00,000 --> 00:00:01,000 Lorem """) def test_text_italic(self): - ttml_doc_str = """ + ttml_doc_str = b"""
@@ -254,12 +263,13 @@ def test_text_italic(self): """ - ttml_doc = et.ElementTree(et.fromstring(ttml_doc_str)) - doc = imsc_reader.to_model(ttml_doc) + doc = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) - srt_from_model = srt_writer.from_model(doc) + buf = io.BytesIO() + srt_writer.from_model(doc, buf) + srt_from_model = buf.getvalue() - self.assertEqual(srt_from_model, """1 + self.assertEqual(srt_from_model, b"""1 00:00:00,000 --> 00:00:01,000 Lorem """) diff --git a/src/test/python/test_vtt_reader.py b/src/test/python/test_vtt_reader.py index 76e57805..90f930ae 100644 --- a/src/test/python/test_vtt_reader.py +++ b/src/test/python/test_vtt_reader.py @@ -38,7 +38,7 @@ class VTTReaderTest(unittest.TestCase): def test_sample(self): - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT 02:00.000 --> 02:05.000 This is bold text @@ -46,7 +46,7 @@ def test_sample(self): 04:00.000 --> 04:05.000 This is italic and this is not """ - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) @@ -56,11 +56,11 @@ def test_samples(self): (name, ext) = os.path.splitext(filename) if ext == ".vtt": with self.subTest(name): - with open(os.path.join(root, filename), encoding="utf-8") as f: + with open(os.path.join(root, filename), "rb") as f: self.assertIsNotNone(to_model(f)) def test_bold(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 02:00.000 --> 02:05.000 This is bold text @@ -74,7 +74,7 @@ def test_bold(self): def test_blank_lines(self): # from https://en.wikipedia.org/wiki/SubRip - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT 1 00:02:16.612 --> 00:02:19.376 @@ -94,13 +94,13 @@ def test_blank_lines(self): """ - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) def test_malformed_blank_lines(self): # from https://github.com/sandflow/ttconv/issues/439 # the first cue should be ignored since it is malformed - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT Kind: captions Language: en @@ -112,7 +112,7 @@ def test_malformed_blank_lines(self): hi everyone today we're going to be """ - doc = to_model(io.StringIO(SAMPLE)) + doc = to_model(io.BytesIO(SAMPLE)) self.assertIsNotNone(doc) body = list(doc.get_body()) self.assertEqual(len(body), 1) @@ -122,7 +122,7 @@ def test_malformed_blank_lines(self): def test_single_line_with_space(self): # from https://github.com/sandflow/ttconv/issues/439 # the first cue is not ignored since the first line contains a single space - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT Kind: captions Language: en @@ -134,7 +134,7 @@ def test_single_line_with_space(self): hi everyone today we're going to be """ - doc = to_model(io.StringIO(SAMPLE)) + doc = to_model(io.BytesIO(SAMPLE)) self.assertIsNotNone(doc) body = list(doc.get_body()) self.assertEqual(len(body), 1) @@ -143,7 +143,7 @@ def test_single_line_with_space(self): def test_toplevel_timestamp_tags(self): # from https://github.com/sandflow/ttconv/issues/439 - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT Kind: captions Language: en @@ -155,7 +155,7 @@ def test_toplevel_timestamp_tags(self): hi everyone today we're going to be """ - doc = to_model(io.StringIO(SAMPLE)) + doc = to_model(io.BytesIO(SAMPLE)) body = list(doc.get_body()) spans_and_brs = list(body[0][0]) self.assertIsNone(spans_and_brs[0].get_begin()) # \x20 @@ -164,7 +164,7 @@ def test_toplevel_timestamp_tags(self): self.assertEqual(spans_and_brs[4].get_begin(), 1.920 - 0.799) # today def test_italic(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 00:02:16.612 --> 00:02:19.376 Hello my name is Bob @@ -178,7 +178,7 @@ def test_italic(self): def test_underline(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 00:02:16.612 --> 00:02:19.376 Hello my name is Bob @@ -193,7 +193,7 @@ def test_underline(self): def test_blue(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 02:00.000 --> 02:05.000 This is bold text @@ -208,7 +208,7 @@ def test_blue(self): def test_bg_blue(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 02:00.000 --> 02:05.000 This is bold text @@ -222,7 +222,7 @@ def test_bg_blue(self): self.fail() def test_lang(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 02:00.000 --> 02:05.000 Spanish as used in Latin America and the Caribbean @@ -237,7 +237,7 @@ def test_lang(self): def test_multiline_tags(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 00:02:16.612 --> 00:02:19.376 Hello my @@ -259,7 +259,7 @@ def test_multiline_tags(self): self.assertIsInstance(next(i), model.Text) def test_long_hours(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 101:00:00.000 --> 101:00:01.000 Hello my name is Bob @@ -277,7 +277,7 @@ def test_long_hours(self): ) def test_ts_tag(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 00:00:01.000 --> 00:00:03.000 Hello my name<00:02.000>is Bob @@ -297,7 +297,7 @@ def test_ts_tag(self): ) def test_ignore_style(self): - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT STYLE ::cue { color:lime } @@ -305,22 +305,22 @@ def test_ignore_style(self): 00:00:00.000 --> 00:00:25.000 Red or green? """ - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) def test_ruby(self): # from WPT (bidi_vertical_lr.vrr) - SAMPLE = """WEBVTT + SAMPLE = b"""WEBVTT 00:00:00.000 --> 00:00:05.000 -.אאab)x +.\xd7\x90\xd7\x90ab)x """ - f = io.StringIO(SAMPLE) + f = io.BytesIO(SAMPLE) self.assertIsNotNone(to_model(f)) def test_line_origin_extent(self): - f = io.StringIO(r"""WEBVTT + f = io.BytesIO(b"""WEBVTT 1 00:00:00.000 --> 00:00:02.000 line:0 @@ -354,13 +354,13 @@ def test_line_origin_extent(self): self.assertEqual(round(regions[3].get_style(styles.StyleProperties.Extent).height.value), 6) def _cue_settings_to_region(self, settings: str) -> model.Region: - f = io.StringIO(f"""WEBVTT + f = io.BytesIO(f"""WEBVTT 1 00:00:00.000 --> 00:00:02.000 {settings} Line 0 starting from top -""") +""".encode('utf-8')) doc = to_model(f) regions = list(doc.iter_regions()) self.assertTrue(len(regions), 1) diff --git a/src/test/python/test_vtt_writer.py b/src/test/python/test_vtt_writer.py index b6e4609b..0bc250ba 100644 --- a/src/test/python/test_vtt_writer.py +++ b/src/test/python/test_vtt_writer.py @@ -27,13 +27,11 @@ # pylint: disable=R0201,C0115,C0116,W0212 +import io import json import os import unittest -import xml.etree.ElementTree as et from fractions import Fraction -from pathlib import Path - import ttconv.imsc.reader as imsc_reader import ttconv.scc.reader as scc_reader import ttconv.stl.reader as stl_reader @@ -89,7 +87,7 @@ def test_vtt_writer(self): span.push_child(Text(doc, "Pellentesque interdum lacinia sollicitudin.")) p.push_child(span) - expected_vtt = """WEBVTT + expected_vtt = b"""WEBVTT 1 00:00:00.000 --> 00:00:02.000 @@ -104,12 +102,14 @@ def test_vtt_writer(self): Pellentesque interdum lacinia sollicitudin. """ - vtt_from_model = vtt_writer.from_model(doc, None) + buf = io.BytesIO() + vtt_writer.from_model(doc, buf) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) def test_position(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -128,7 +128,7 @@ def test_position(self): """ - expected_vtt="""WEBVTT + expected_vtt=b"""WEBVTT 1 00:00:03.500 --> 00:00:12.000 line:90%,end @@ -140,18 +140,22 @@ def test_position(self): Cool, got it, will do it by end of next week. """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) config = VTTWriterConfiguration() config.line_position = True - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) config = VTTWriterConfiguration.parse(json.loads('{"line_position":true}')) - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) def test_align(self): - ttml_doc_str = """ + ttml_doc_str = b""" @@ -174,7 +178,7 @@ def test_align(self): """ - expected_vtt="""WEBVTT + expected_vtt=b"""WEBVTT 1 00:00:03.500 --> 00:00:12.000 align:center @@ -194,18 +198,22 @@ def test_align(self): Good. """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) config = VTTWriterConfiguration() config.text_align = True - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) config = VTTWriterConfiguration.parse(json.loads('{"text_align":true}')) - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) def test_cue_id(self): - ttml_doc_str = """ + ttml_doc_str = b"""
@@ -215,7 +223,7 @@ def test_cue_id(self): """ - expected_vtt="""WEBVTT + expected_vtt=b"""WEBVTT 00:00:03.500 --> 00:00:12.000 Only one or two short samples are needed @@ -225,14 +233,18 @@ def test_cue_id(self): Cool, got it, will do it by end of next week. """ - model = imsc_reader.to_model(et.ElementTree(et.fromstring(ttml_doc_str))) + model = imsc_reader.to_model(io.BytesIO(ttml_doc_str)) config = VTTWriterConfiguration() config.cue_id = False - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) config = VTTWriterConfiguration.parse(json.loads('{"cue_id":false}')) - vtt_from_model = vtt_writer.from_model(model, config) + buf = io.BytesIO() + vtt_writer.from_model(model, buf, config) + vtt_from_model = buf.getvalue() self.assertEqual(expected_vtt, vtt_from_model) def test_scc_test_suite(self): @@ -242,9 +254,11 @@ def test_scc_test_suite(self): if ext == ".scc": with self.subTest(name): path = os.path.join(root, filename) - scc_content = Path(path).read_text() - test_model = scc_reader.to_model(scc_content) - vtt_from_model = vtt_writer.from_model(test_model, None) + with open(path, 'rb') as f: + test_model = scc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self.assertTrue(len(vtt_from_model) > 0, msg=f"Could not convert {path}") self._check_output_vtt(test_model, vtt_from_model, path) @@ -257,7 +271,9 @@ def test_irt_stl_test_suite(self): path = os.path.join(root, filename) with open(path, "rb") as stl_content: test_model = stl_reader.to_model(stl_content) - vtt_from_model = vtt_writer.from_model(test_model, None) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self.assertTrue(len(vtt_from_model) > 0, msg=f"Could not convert {path}") self._check_output_vtt(test_model, vtt_from_model, path) @@ -270,7 +286,9 @@ def test_sandflow_stl_test_suite(self): path = os.path.join(root, filename) with open(path, "rb") as stl_content: test_model = stl_reader.to_model(stl_content) - vtt_from_model = vtt_writer.from_model(test_model, None) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self.assertTrue(len(vtt_from_model) > 0, msg=f"Could not convert {path}") self._check_output_vtt(test_model, vtt_from_model, path) @@ -281,9 +299,11 @@ def test_imsc_1_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - vtt_from_model = vtt_writer.from_model(test_model, None) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self._check_output_vtt(test_model, vtt_from_model, path) @unittest.skip("Too long to process") @@ -294,9 +314,11 @@ def test_imsc_1_1_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - vtt_from_model = vtt_writer.from_model(test_model, None) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self._check_output_vtt(test_model, vtt_from_model, path) @unittest.skip("IMSC 1.2 is not supported") @@ -307,9 +329,11 @@ def test_imsc_1_2_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - vtt_from_model =vtt_writer.from_model(test_model, None) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self._check_output_vtt(test_model, vtt_from_model, path) @unittest.skip("IMSC 1.3 is not supported") @@ -320,9 +344,11 @@ def test_imsc_1_3_test_suite(self): if ext == ".ttml": with self.subTest(name): path = os.path.join(root, filename) - tree = et.parse(path) - test_model = imsc_reader.to_model(tree) - vtt_from_model =vtt_writer.from_model(test_model, None) + with open(path, 'rb') as f: + test_model = imsc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(test_model, buf) + vtt_from_model = buf.getvalue() self._check_output_vtt(test_model, vtt_from_model, path) # # Utility functions @@ -357,18 +383,20 @@ def _has_document_paragraphs(self, doc: ContentDocument) -> bool: return paragraphs - def _check_output_vtt(self, model: ContentDocument, vtt: str, path: str): + def _check_output_vtt(self, model: ContentDocument, vtt: bytes, path: str): if self._has_document_paragraphs(model): self.assertTrue(len(vtt) > 0, msg=f"Could not convert {path}") else: self.assertEqual(8, len(vtt), msg=f"Could not convert {path}") def test_empty_isds(self): - tree = et.parse('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming010.ttml') - doc = imsc_reader.to_model(tree) - srt_from_model = vtt_writer.from_model(doc) + with open('src/test/resources/ttml/imsc-tests/imsc1/ttml/timing/BasicTiming010.ttml', 'rb') as f: + doc = imsc_reader.to_model(f) + buf = io.BytesIO() + vtt_writer.from_model(doc, buf) + srt_from_model = buf.getvalue() - self.assertEqual(srt_from_model, """WEBVTT + self.assertEqual(srt_from_model, b"""WEBVTT 1 00:00:10.000 --> 00:00:24.400