Skip to content

Commit 9980cb5

Browse files
authored
Fixes for Python 3.14
- Fix codecs deprecation - Fix issue with unclosed `<![` - Fix issue with unclosed HTML tag `<foo` - Fix issue with unclosed comments - Add tests which run on the Python 3.14 beta which should automatically update after release Fixes #1537
1 parent 3561310 commit 9980cb5

File tree

7 files changed

+41
-10
lines changed

7 files changed

+41
-10
lines changed

.github/workflows/tox.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
fail-fast: false
2121
max-parallel: 4
2222
matrix:
23-
tox-env: [py39, py310, py311, py312, py313, pypy39, pypy310, pygments]
23+
tox-env: [py39, py310, py311, py312, py313, py314, pypy39, pypy310, pygments]
2424
include:
2525
- tox-env: py39
2626
python-version: '3.9'
@@ -32,13 +32,14 @@ jobs:
3232
python-version: '3.12'
3333
- tox-env: py313
3434
python-version: '3.13'
35+
- tox-env: py314
36+
python-version: '3.14'
3537
- tox-env: pypy39
3638
python-version: pypy-3.9
3739
- tox-env: pypy310
3840
python-version: pypy-3.10
3941
- tox-env: pygments
4042
python-version: '3.11'
41-
4243
env:
4344
TOXENV: ${{ matrix.tox-env }}
4445

docs/changelog.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@ and this project adheres to the
1010
[Python Version Specification]: https://packaging.python.org/en/latest/specifications/version-specifiers/.
1111
See the [Contributing Guide](contributing.md) for details.
1212

13-
## [unreleased]
13+
## [Unreleased]
14+
15+
### Fixed
16+
17+
* Fix `codecs` deprecation in Python 3.14.
18+
* Fix issue with unclosed comment parsing in Python 3.14.
19+
* Fix issue with unclosed declarations in Python 3.14.
20+
* Fix issue with unclosed HTML tag `<foo` and Python 3.14.
1421

1522
## [3.8.1] - 2025-06-18
1623

markdown/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import sys
2323
import optparse
24-
import codecs
2524
import warnings
2625
import markdown
2726
try:
@@ -100,7 +99,7 @@ def parse_options(args=None, values=None):
10099

101100
extension_configs = {}
102101
if options.configfile:
103-
with codecs.open(
102+
with open(
104103
options.configfile, mode="r", encoding=options.encoding
105104
) as fp:
106105
try:

markdown/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def convertFile(
417417
# Read the source
418418
if input:
419419
if isinstance(input, str):
420-
input_file = codecs.open(input, mode="r", encoding=encoding)
420+
input_file = open(input, mode="r", encoding=encoding)
421421
else:
422422
input_file = codecs.getreader(encoding)(input)
423423
text = input_file.read()

markdown/extensions/md_in_html.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,11 @@ def parse_html_declaration(self, i: int) -> int:
283283
if self.rawdata[i:i+3] == '<![' and not self.rawdata[i:i+9] == '<![CDATA[':
284284
# We have encountered the bug in #1534 (Python bug `gh-77057`).
285285
# Provide an override until we drop support for Python < 3.13.
286-
return self.parse_bogus_comment(i)
286+
result = self.parse_bogus_comment(i)
287+
if result == -1:
288+
self.handle_data(self.rawdata[i:i + 1])
289+
return i + 1
290+
return result
287291
# The same override exists in `HTMLExtractor` without the check
288292
# for `mdstack`. Therefore, use parent of `HTMLExtractor` instead.
289293
return super(HTMLExtractor, self).parse_html_declaration(i)

markdown/htmlparser.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ def __init__(self, md: Markdown, *args, **kwargs):
9393

9494
self.lineno_start_cache = [0]
9595

96+
self.override_comment_update = False
97+
9698
# This calls self.reset
9799
super().__init__(*args, **kwargs)
98100
self.md = md
@@ -253,8 +255,21 @@ def handle_entityref(self, name: str):
253255
self.handle_empty_tag('&{};'.format(name), is_block=False)
254256

255257
def handle_comment(self, data: str):
258+
# Check if the comment is unclosed, if so, we need to override position
259+
i = self.line_offset + self.offset + len(data) + 4
260+
if self.rawdata[i:i + 3] != '-->':
261+
self.handle_data('<')
262+
self.override_comment_update = True
263+
return
256264
self.handle_empty_tag('<!--{}-->'.format(data), is_block=True)
257265

266+
def updatepos(self, i: int, j: int) -> int:
267+
if self.override_comment_update:
268+
self.override_comment_update = False
269+
i = 0
270+
j = 1
271+
return super().updatepos(i, j)
272+
258273
def handle_decl(self, data: str):
259274
self.handle_empty_tag('<!{}>'.format(data), is_block=True)
260275

@@ -278,7 +293,11 @@ def parse_html_declaration(self, i: int) -> int:
278293
if self.rawdata[i:i+3] == '<![' and not self.rawdata[i:i+9] == '<![CDATA[':
279294
# We have encountered the bug in #1534 (Python bug `gh-77057`).
280295
# Provide an override until we drop support for Python < 3.13.
281-
return self.parse_bogus_comment(i)
296+
result = self.parse_bogus_comment(i)
297+
if result == -1:
298+
self.handle_data(self.rawdata[i:i + 1])
299+
return i + 1
300+
return result
282301
return super().parse_html_declaration(i)
283302
# This is not the beginning of a raw block so treat as plain data
284303
# and avoid consuming any tags which may follow (see #1066).
@@ -313,7 +332,8 @@ def parse_starttag(self, i: int) -> int: # pragma: no cover
313332
self.__starttag_text = None
314333
endpos = self.check_for_whole_start_tag(i)
315334
if endpos < 0:
316-
return endpos
335+
self.handle_data(self.rawdata[i:i + 1])
336+
return i + 1
317337
rawdata = self.rawdata
318338
self.__starttag_text = rawdata[i:endpos]
319339

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py{39, 310, 311, 312, 313}, pypy{39, 310}, pygments, flake8, checkspelling, pep517check, checklinks
2+
envlist = py{39, 310, 311, 312, 313, py314}, pypy{39, 310}, pygments, flake8, checkspelling, pep517check, checklinks
33
isolated_build = True
44

55
[testenv]

0 commit comments

Comments
 (0)