From 5eb46d2e6e6082c421aeed94d8c8271ce74544da Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Tue, 28 Jul 2026 07:34:49 -0500 Subject: [PATCH 1/2] Migrate from `sgmllib3k` to `feedparser-sgmllib` In addition: * Drop support for Python 3.9 and lower. * Fix setuptools build warnings. --- feedparser/html.py | 33 +++------------------------------ feedparser/sgml.py | 37 +++---------------------------------- pyproject.toml | 3 +++ setup.py | 14 +++++++------- tox.ini | 2 +- 5 files changed, 17 insertions(+), 72 deletions(-) create mode 100644 pyproject.toml diff --git a/feedparser/html.py b/feedparser/html.py index a39e5504..908dde87 100644 --- a/feedparser/html.py +++ b/feedparser/html.py @@ -28,7 +28,7 @@ import html.entities import re -from .sgml import * +from .sgml import sgmllib _cp1252 = { 128: '\u20ac', # euro sign @@ -61,7 +61,7 @@ } -class _BaseHTMLProcessor(sgmllib.SGMLParser, object): +class _BaseHTMLProcessor(sgmllib.SGMLParser): special = re.compile("""[<>'"]""") bare_ampersand = re.compile(r"&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)") elements_no_end_tag = { @@ -109,35 +109,8 @@ def _shorttag_replace(self, match): else: 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. - try: - goahead.__code__ = sgmllib.SGMLParser.goahead.__code__ - except AttributeError: - # Python 2 - # noinspection PyUnresolvedReferences - goahead.func_code = sgmllib.SGMLParser.goahead.func_code - - def __parse_starttag(self, i): - raise NotImplementedError - - # Replace __parse_starttag with SGMLParser's parse_starttag() code object. - try: - __parse_starttag.__code__ = sgmllib.SGMLParser.parse_starttag.__code__ - except AttributeError: - # Python 2 - # noinspection PyUnresolvedReferences - __parse_starttag.func_code = sgmllib.SGMLParser.parse_starttag.func_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 5d55a40a..ac69f15a 100644 --- a/feedparser/sgml.py +++ b/feedparser/sgml.py @@ -27,43 +27,12 @@ import re -import sgmllib +import feedparser.sgmllib as sgmllib __all__ = [ - 'sgmllib', - 'charref', - 'tagfind', - 'attrfind', - 'entityref', - 'incomplete', - 'interesting', - 'shorttag', - 'shorttagopen', - 'starttagopen', - 'endbracket', + "sgmllib", ] -# 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() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4ceecf5e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=77.0.3"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 6886ec32..3fde4bf8 100644 --- a/setup.py +++ b/setup.py @@ -58,19 +58,19 @@ download_url='https://pypi.python.org/pypi/feedparser', platforms=['POSIX', 'Windows'], packages=['feedparser', 'feedparser.datetimes', 'feedparser.namespaces', 'feedparser.parsers'], - install_requires=['sgmllib3k'], - python_requires='>=3.6', + install_requires=['feedparser-sgmllib (>=1, <2)'], + python_requires='>=3.10', keywords=['atom', 'cdf', 'feed', 'parser', 'rdf', 'rss'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Text Processing :: Markup :: XML', ], diff --git a/tox.ini b/tox.ini index 6e59b702..1339fb2d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py{36, 37, 38, 39}{-chardet, } +envlist = py{3.10, 3.11, 3.12, 3.13, 3.14}{-chardet, } skip_missing_interpreters = True [testenv] From 587b7d69e2d42291399177106964acb50245ae34 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Tue, 28 Jul 2026 07:35:17 -0500 Subject: [PATCH 2/2] Update project metadata --- NEWS | 5 +++++ feedparser/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index fd6c9238..05dca713 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ coming in the next release: +6.0.13 - 28 July 2026 + * Drop support for Python 3.9 and lower. + * Migrate from ``sgmllib3k`` to ``feedparser-sgmllib``. + * Resolve package build warnings. + 6.0.12 - 10 September 2025 * Fix an ``AssertionError`` crash that occurs with Python 3.10 and higher. (#304) * Fix a ``DeprecationWarning`` thrown during calls to ``re.sub``. (#389) diff --git a/feedparser/__init__.py b/feedparser/__init__.py index b1b69fc8..0be2b2b8 100644 --- a/feedparser/__init__.py +++ b/feedparser/__init__.py @@ -32,7 +32,7 @@ __author__ = 'Kurt McKee ' __license__ = 'BSD 2-clause' -__version__ = '6.0.12' +__version__ = '6.0.13' # HTTP "User-Agent" header to send to servers when downloading feeds. # If you are embedding feedparser in a larger application, you should