diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 45cb419e..e9a547df 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,24 @@ Please see the fragment files in the `changelog.d directory`_. .. scriv-insert-here +6.0.13 - 2026-07-28 +=================== + +Python support +-------------- + +* Drop support for Python 3.9 and lower. + +Changed +------- + +* Migrate from ``sgmllib3k`` to ``feedparser-sgmllib``. + +Fixed +----- + +* Resolve package build warnings. + 6.0.12 - 2025-09-10 =================== diff --git a/changelog.d/20230219_143414_kurtmckee_update_python_support.rst b/changelog.d/20230219_143414_kurtmckee_update_python_support.rst index 64617c55..0db3bdc7 100644 --- a/changelog.d/20230219_143414_kurtmckee_update_python_support.rst +++ b/changelog.d/20230219_143414_kurtmckee_update_python_support.rst @@ -2,4 +2,3 @@ Python support -------------- * Support Python 3.11. -* Drop support for Python 3.6. diff --git a/changelog.d/20230511_103746_kurtmckee_drop_python_3_7.rst b/changelog.d/20230511_103746_kurtmckee_drop_python_3_7.rst deleted file mode 100644 index 805cbe08..00000000 --- a/changelog.d/20230511_103746_kurtmckee_drop_python_3_7.rst +++ /dev/null @@ -1,4 +0,0 @@ -Python support --------------- - -* Drop support for Python 3.7. diff --git a/changelog.d/20240806_090500_kurtmckee_updates.rst b/changelog.d/20240806_090500_kurtmckee_updates.rst index b4c3a4ce..098ee096 100644 --- a/changelog.d/20240806_090500_kurtmckee_updates.rst +++ b/changelog.d/20240806_090500_kurtmckee_updates.rst @@ -1,8 +1,3 @@ -Python support --------------- - -* Drop Python 3.8 support. - Project development ------------------- diff --git a/changelog.d/20250910_091527_kurtmckee_drop_py3_9_support.rst b/changelog.d/20250910_091527_kurtmckee_drop_py3_9_support.rst index fff9afe3..e9ff2a59 100644 --- a/changelog.d/20250910_091527_kurtmckee_drop_py3_9_support.rst +++ b/changelog.d/20250910_091527_kurtmckee_drop_py3_9_support.rst @@ -1,8 +1,7 @@ Python support -------------- -* Drop Python 3.9 support. -* Begin testing Python 3.14 beta releases. +* Support Python 3.14. Project development diff --git a/docs/introduction.rst b/docs/introduction.rst index 28648baa..ed9f3a14 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -12,10 +12,8 @@ Notation)` feeds. It also parses several popular extension modules, including Dublin Core and Apple's :program:`iTunes` extensions. -To use :program:`Universal Feed Parser`, you will need :program:`Python` 3.8 or -later. :program:`Universal Feed Parser` is not meant -to run standalone; it is a module for you to use as part of a larger -:program:`Python` program. +:program:`Universal Feed Parser` is not meant to run standalone; +it is a module for you to use as part of a larger :program:`Python` program. :program:`Universal Feed Parser` is easy to use; it has one primary public function, ``parse``. ``parse`` takes a number of arguments, but only one is diff --git a/feedparser/__init__.py b/feedparser/__init__.py index f9d15494..7d811d28 100644 --- a/feedparser/__init__.py +++ b/feedparser/__init__.py @@ -38,7 +38,7 @@ __author__ = "Kurt McKee " __license__ = "BSD 2-clause" -__version__ = "6.0.12" +__version__ = "6.0.13" # If you want feedparser to automatically resolve all relative URIs, set this # to 1. diff --git a/feedparser/html.py b/feedparser/html.py index a774530f..fafe74f5 100644 --- a/feedparser/html.py +++ b/feedparser/html.py @@ -28,20 +28,7 @@ import html.entities import re -# These items must all be imported into this module due to .__code__ replacements. -from .sgml import ( # noqa: F401 - attrfind, - charref, - endbracket, - entityref, - incomplete, - interesting, - sgmllib, - shorttag, - shorttagopen, - starttagopen, - tagfind, -) +from .sgml import sgmllib _cp1252 = { 128: "\u20ac", # euro sign @@ -121,25 +108,8 @@ def _shorttag_replace(self, match): return "<" + tag + " />" return "<" + tag + ">" - # By declaring these methods and overriding their compiled code - # with the code from sgmllib, the original code will execute in - # feedparser's scope instead of sgmllib's. This means that the - # `tagfind` and `charref` regular expressions will be found as - # they're declared above, not as they're declared in sgmllib. - def goahead(self, i): - raise NotImplementedError - - # Replace goahead with SGMLParser's goahead() code object. - goahead.__code__ = sgmllib.SGMLParser.goahead.__code__ - - def __parse_starttag(self, i): - raise NotImplementedError - - # Replace __parse_starttag with SGMLParser's parse_starttag() code object. - __parse_starttag.__code__ = sgmllib.SGMLParser.parse_starttag.__code__ - def parse_starttag(self, i): - j = self.__parse_starttag(i) + j = super().parse_starttag(i) if self._type == "application/xhtml+xml": if j > 2 and self.rawdata[j - 2 : j] == "/>": self.unknown_endtag(self.lasttag) diff --git a/feedparser/sgml.py b/feedparser/sgml.py index 5b2923cc..fedd7bc0 100644 --- a/feedparser/sgml.py +++ b/feedparser/sgml.py @@ -27,43 +27,12 @@ import re -import sgmllib # type: ignore[import] +import feedparser.sgmllib as sgmllib __all__ = [ "sgmllib", - "charref", - "tagfind", - "attrfind", - "entityref", - "incomplete", - "interesting", - "shorttag", - "shorttagopen", - "starttagopen", - "endbracket", ] -# sgmllib defines a number of module-level regular expressions that are -# insufficient for the XML parsing feedparser needs. Rather than modify -# the variables directly in sgmllib, they're defined here using the same -# names, and the compiled code objects of several sgmllib.SGMLParser -# methods are copied into _BaseHTMLProcessor so that they execute in -# feedparser's scope instead of sgmllib's scope. -charref = re.compile(r"&#(\d+|[xX][0-9a-fA-F]+);") -tagfind = re.compile(r"[a-zA-Z][-_.:a-zA-Z0-9]*") -attrfind = re.compile( - r"""\s*([a-zA-Z_][-:.a-zA-Z_0-9]*)[$]?(\s*=\s*""" - r"""('[^']*'|"[^"]*"|[][\-a-zA-Z0-9./,:;+*%?!&$()_#=~'"@]*))?""" -) - -# Unfortunately, these must be copied over to prevent NameError exceptions -entityref = sgmllib.entityref -incomplete = sgmllib.incomplete -interesting = sgmllib.interesting -shorttag = sgmllib.shorttag -shorttagopen = sgmllib.shorttagopen -starttagopen = sgmllib.starttagopen - class _EndBracketRegEx: def __init__(self): @@ -95,4 +64,4 @@ def start(self, n): return self.match.end(n) -endbracket = _EndBracketRegEx() +sgmllib.endbracket = _EndBracketRegEx() # type: ignore[assignment] diff --git a/pyproject.toml b/pyproject.toml index dbc32538..71ac34e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "feedparser" -version = "6.0.11" +version = "6.0.13" description = "Parse Atom/RSS/JSON feeds in Python" readme = "README.rst" requires-python = ">=3.10" @@ -15,7 +15,7 @@ classifiers = [ "Topic :: Text Processing :: Markup", ] dependencies = [ - "sgmllib3k==1.0.0", + "feedparser-sgmllib (>=1, <2)", "requests>=2.20.0", ] diff --git a/tests/conftest.py b/tests/conftest.py index 2cf47a03..c9d54be8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,32 @@ import responses +# TODO: tag=bad-layout +# +# The `sys.path` must be modified to guarantee that +# the repository's root directory IS NOT present +# at the start of the import path list. +# +# If the path appears at the beginning of `sys.path`, +# it will breaks imports because of the namespace package +# feedparser-sgmllib. +# +# After migrating to a `src/` layout, remove this code block. +# +# ---- 8< ---- +def rewrite_sys_path(): + import pathlib + import sys + + root = str(pathlib.Path(__file__).parent.parent.absolute()) + if sys.path[0] == root: + sys.path.pop(0) + + +rewrite_sys_path() +# ---- >8 ---- + + @pytest.fixture def use_loose_parser(monkeypatch): import feedparser.api diff --git a/tox.ini b/tox.ini index 341ead99..789a0c64 100644 --- a/tox.ini +++ b/tox.ini @@ -46,7 +46,16 @@ deps = -r requirements/test/requirements.txt chardet: chardet commands = - coverage run -m pytest {posargs:} + # TODO: tag=bad-layout + # + # `--import-mode=append` is required to prevent pytest + # from inserting the repository's root directory + # at the beginning of `sys.path`, which breaks imports + # because of the namespace package feedparser-sgmllib. + # + # After migrating to a `src/` layout, remove the option. + # + coverage run -m pytest --import-mode=append {posargs:} [testenv:coverage_base]