diff --git a/poetry.lock b/poetry.lock index 4a46eac..cc016e4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "certifi" @@ -219,6 +219,7 @@ description = "XML bomb protection for Python stdlib modules" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" groups = ["main"] +markers = "python_version < \"3.11\"" files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, @@ -535,4 +536,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = ">=3.8,<3.15" -content-hash = "34bdc0ef7b905a807195902bdfdd5db84e0bc5ccd0f6cb1883c35004a6e06dd6" +content-hash = "2e9a28545c18350c0021dd090ea3e8c8ea1b36df1a67557dcaed2261baa0eacb" diff --git a/pyproject.toml b/pyproject.toml index d0bec13..84945f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ precommit.shell = "poe format && poe lint && poe coverage" [tool.poetry.dependencies] python = ">=3.8,<3.15" requests = "*" -defusedxml = "^0.7.1" +defusedxml = { version = "^0.7.1", python = "<3.11" } [tool.poetry.group.test] optional = true @@ -93,4 +93,4 @@ exclude_lines = [ "@abstractclassmethod", "@abstractstaticmethod" ] -show_missing = true \ No newline at end of file +show_missing = true diff --git a/youtube_transcript_api/_transcripts.py b/youtube_transcript_api/_transcripts.py index 55baa42..3671c5a 100644 --- a/youtube_transcript_api/_transcripts.py +++ b/youtube_transcript_api/_transcripts.py @@ -1,11 +1,15 @@ from dataclasses import dataclass, asdict from enum import Enum from itertools import chain +import sys from html import unescape from typing import List, Dict, Iterator, Iterable, Pattern, Optional -from defusedxml import ElementTree +if sys.version_info < (3, 11): + from defusedxml import ElementTree +else: + from xml.etree import ElementTree import re diff --git a/youtube_transcript_api/test/test_api.py b/youtube_transcript_api/test/test_api.py index 6006034..4c786d8 100644 --- a/youtube_transcript_api/test/test_api.py +++ b/youtube_transcript_api/test/test_api.py @@ -1,5 +1,8 @@ +import importlib import pytest import os +import sys +import types from pathlib import Path from unittest import TestCase from unittest.mock import patch @@ -7,6 +10,7 @@ import requests import responses +import youtube_transcript_api._transcripts as transcripts_module from youtube_transcript_api import ( YouTubeTranscriptApi, @@ -97,6 +101,41 @@ def test_fetch(self): self.ref_transcript, ) + def test_fetch__uses_expected_xml_backend_for_python_version(self): + expected_backend = ( + "defusedxml.ElementTree" + if sys.version_info < (3, 11) + else "xml.etree.ElementTree" + ) + + self.assertEqual(transcripts_module.ElementTree.__name__, expected_backend) + + def test_fetch__uses_defusedxml_backend_for_python_3_10_and_older(self): + fake_element_tree = object() + fake_defusedxml = types.ModuleType("defusedxml") + fake_defusedxml.ElementTree = fake_element_tree + + with patch.object(sys, "version_info", (3, 10)): + with patch.dict(sys.modules, {"defusedxml": fake_defusedxml}): + spec = importlib.util.spec_from_file_location( + "youtube_transcript_api._transcripts_py310", + transcripts_module.__file__, + ) + reloaded_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(reloaded_module) + + self.assertIs(reloaded_module.ElementTree, fake_element_tree) + + def test_fetch__uses_stdlib_xml_backend_for_python_3_11_and_newer(self): + with patch.object(sys, "version_info", (3, 11)): + spec = importlib.util.spec_from_file_location( + "youtube_transcript_api._transcripts_py311", transcripts_module.__file__ + ) + reloaded_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(reloaded_module) + + self.assertEqual(reloaded_module.ElementTree.__name__, "xml.etree.ElementTree") + def test_fetch_formatted(self): transcript = YouTubeTranscriptApi().fetch( "GJLlxj_dtq8", preserve_formatting=True