On Wed, Sep 09, 2026 at 09:53:07AM +0100, José Matos wrote:
> > I am happy to ditch, but I saw explicit checks on python 2 while
> > going through the code yesterday, eg in Graphicsconverter.
>
> When I did the change I have only checked the python code, not C++.
> Good catch.
I see. I instructed our silicon colleague to look around for other remnants
if you interested. Below the findings and patches avaliable for adoption.
I leave it to you and Kornel to review and decide what to ship and what not
regarding python2 transition. The case for lyxblogger seems clear though ;)
We could probably let clause to go through each item in configure.py and decide
on obsoletness... Some other day ;)
> The new line is probably an artifact of the mail composer. :-(
I thought so. I'll push my version then.
Pavel
Python 2 remnants in the LyX tree -- scan of 2026-09-09
=======================================================
Scope: 80 .py files -- lib/scripts (29), lib/lyx2lyx (30), development (18), po
(2), lib/configure.py.
Patterns searched: from __future__, unicode(), basestring,
iteritems/iterkeys/itervalues, has_key,
unichr, xrange, raw_input, py2 "except X, e", py2 print statements,
sys.version_info[0], PY2,
python2, 2to3.
Shipped code -- lib/scripts/, lib/lyx2lyx/, lib/configure.py -- has NO remnants
at all.
Everything below lives under development/ .
A. INERT -- dead code, never evaluated on python 3. Removing it is tidying,
not fixing.
------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------+--------------------------------------------------------------+
| remnant | file(s)
|
+-------------------------------------------------------+--------------------------------------------------------------+
| from __future__ import print_function | cmake/po/cat.py
|
| |
cmake/doc/ReplaceValues.py |
| verified: imports fine on py3, pure no-op |
tools/generate_symbols_images.py |
| |
tools/generate_symbols_list.py |
| |
tools/unicodesymbols.py |
| | autotests/keytest.py
|
+-------------------------------------------------------+--------------------------------------------------------------+
| if sys.version_info[0] < 3: c = unichr(i) |
tools/unicodesymbols.py:73-76 |
| branch not taken (test is False); collapses to chr(i) |
|
+-------------------------------------------------------+--------------------------------------------------------------+
| if sys.version_info[0] < 3: mode = "rU" | cmake/po/cat.py:32-41
|
| branch not taken; collapses to mode = "r" |
|
+-------------------------------------------------------+--------------------------------------------------------------+
Both dead branches WOULD fail if ever reached: unichr() -> NameError, and
open(f, "rU") -> ValueError on
py3.11, where "U" mode was removed. Today nothing reaches them.
B. ACTIVELY BREAKS -- runs under python 2, or fails to start at all.
------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------+--------------------------------------------------------------+
| problem | file(s) / site
|
+-------------------------------------------------------+--------------------------------------------------------------+
| COMMAND python ${KEYTEST} |
autotests/single-test.cmake:104 |
| |
|
| Bare "python" -- not python3, not | The add_test() in
autotests/CMakeLists.txt also fails to |
| ${LYX_PYTHON_EXECUTABLE}. This is the ONLY bare | pass the variable
through. |
| "COMMAND python" left in the whole cmake tree; every |
|
| other autotest script already uses | FIXED BY:
misc/dev-python3-keytest-fix.patch |
| ${LYX_PYTHON_EXECUTABLE}, which the build detects as |
|
| python >= 3.8. On this box /usr/bin/python -> |
|
| python2; on distros shipping no /usr/bin/python at |
|
| all (Debian 12+ by default) the keytest dies with |
|
| "command not found" instead of failing honestly. |
|
+-------------------------------------------------------+--------------------------------------------------------------+
| #!/usr/bin/python shebang, file is chmod +x, so | autotests/keytest.py
|
| ./keytest.py picks up python2 here | py3-clean, so
switching is safe. |
| | FIXED BY:
misc/dev-python3-keytest-fix.patch |
+-------------------------------------------------------+--------------------------------------------------------------+
| #!/usr/bin/python shebang AND the scripts are | keystest/keytest.py
|
| py2-ONLY (bare print statements -> SyntaxError on |
keystest/cache-bisect.py |
| py3). DO NOT just bump the shebang: that turns a |
keystest/make_screenshot_html.py |
| working script into a SyntaxError. |
|
| | NOT FIXED -- needs a
real py2->py3 port, or removal. Not |
| | referenced by
cmake/ctest; only Makefile.am EXTRA_DIST plus |
| | the "keystest" make
target, which runs keystest/lyx_make.sh. |
+-------------------------------------------------------+--------------------------------------------------------------+
| \converter xhtml blog "python -m lyxblogger $$i" |
lib/configure.py:1036-1040 |
| |
|
| Bare "python" in a SHIPPED converter -- the only one; | LyXBlogger is dead
upstream: last commit 2012-05-20, last |
| every other converter uses ${python}. Worse, the | release 0.42 (Jun
2012), never on PyPI. It cannot run on |
| module behind it is python 2 ONLY: implicit relative | py3 at all --
verified on the 0.42 tarball: |
| imports (from misc import pr3, import wordpresslib) |
LyxBlog.handle_exceptions -> ModuleNotFoundError: 'misc' |
| were removed in py3 by PEP 328. Its installer also | LyxBlog.cat ->
ModuleNotFoundError: 'wordpresslib' |
| uses distutils, gone in py3.12. So checkModule() | and it posts over
WordPress XML-RPC, which modern hosts |
| already fails on any modern box and nothing gets | disable in favour of
the REST API. |
| registered -- the bare "python" is doubly dead. |
|
| | FIXED BY:
misc/configure-drop-lyxblogger.patch (removal, not |
| Do NOT "fix" it to ${python}: that would aim a | a repair -- pointing
it at py3 guarantees a failure instead |
| py2-only module at py3 and turn a no-op into a | of a no-op). Removes
a user-visible export format, so it |
| guaranteed failure. | carries a
RELEASE-NOTES line. |
+-------------------------------------------------------+--------------------------------------------------------------+
Verified on this machine: /usr/bin/python -> python2, /usr/bin/python3 exists.
Patches prepared
------------------------------------------------------------------------------------------------------------------------
misc/dev-python3-keytest-fix.patch category B -- the real fix
(3 files, +3/-2)
misc/configure-drop-lyxblogger.patch category B -- removal
(2 files, +5/-6)
misc/dev-python2-deadcode-cleanup.patch category A -- tidying
(6 files, +4/-21)
All three apply cleanly on master in any order; every touched script compiles
under python 3, and cat.py
was smoke-tested (CRLF in -> LF out, which is what it exists for).
>From 4dbf701cd7bfd2bec34de6ff026d88bf70201a6d Mon Sep 17 00:00:00 2001
Date: Wed, 9 Sep 2026 10:34:42 +0200
Subject: [PATCH] Do not invoke the keytest through a bare "python"
development/autotests/single-test.cmake ran the keytest script as
"COMMAND python ${KEYTEST}". That is the only bare "COMMAND python" left
in the cmake tree; every other autotest script already uses
${LYX_PYTHON_EXECUTABLE}, which the build detects as python >= 3.8.
A bare "python" resolves to python2 where /usr/bin/python still points
there, and does not exist at all on distributions that ship no
/usr/bin/python (Debian 12 and later by default), where the test fails
to start instead of failing honestly.
Use ${LYX_PYTHON_EXECUTABLE}, pass it through from the add_test call, and
bump the keytest.py shebang to python3 for direct invocation.
The three py2-only scripts in development/keystest/ keep their shebang:
they still use print statements, so pointing them at python3 would turn a
working script into a SyntaxError. They need a port, not a shebang.
Assisted-by: Claude Opus 5
---
development/autotests/CMakeLists.txt | 1 +
development/autotests/keytest.py | 2 +-
development/autotests/single-test.cmake | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/development/autotests/CMakeLists.txt b/development/autotests/CMakeLists.txt
index 7116d7a4ad..0abca9b998 100644
--- a/development/autotests/CMakeLists.txt
+++ b/development/autotests/CMakeLists.txt
@@ -103,6 +103,7 @@ if(QT_USES_X11)
COMMAND ${CMAKE_COMMAND}
-DLYX_TESTS_USERDIR=${LYX_TESTS_USERDIR}
-DAUTOTEST_ROOT=${TOP_SRC_DIR}/development/autotests
+ -DLYX_PYTHON_EXECUTABLE=${LYX_PYTHON_EXECUTABLE}
-DPO_BUILD_DIR=${TOP_BINARY_DIR}/po
-DKEYTEST_INFILE=${_tf}
-DBINDIR=$<TARGET_FILE_DIR:${_lyx}>
diff --git a/development/autotests/keytest.py b/development/autotests/keytest.py
index ce3b245653..deb970219b 100755
--- a/development/autotests/keytest.py
+++ b/development/autotests/keytest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
# -*- coding: utf-8 -*-
# This script generates hundreds of random keypresses per second,
# and sends them to the lyx window
diff --git a/development/autotests/single-test.cmake b/development/autotests/single-test.cmake
index 871bf11540..10a5506545 100755
--- a/development/autotests/single-test.cmake
+++ b/development/autotests/single-test.cmake
@@ -101,7 +101,7 @@ if(EXISTS "${AUTOTEST_ROOT}/${_jj}.lyx")
endif()
execute_process(
- COMMAND python ${KEYTEST}
+ COMMAND ${LYX_PYTHON_EXECUTABLE} ${KEYTEST}
RESULT_VARIABLE KEYTEST_RES)
if(KEYTEST_RES)
message(FATAL_ERROR "KEYTEST failed: KEYTEST_RES=${KEYTEST_RES}")
--
2.39.5
>From e2348adfa760c0e1ff9b6cf9925a36e6cb22312c Mon Sep 17 00:00:00 2001
Date: Wed, 9 Sep 2026 11:22:30 +0200
Subject: [PATCH] Drop LyXBlogger support
configure.py probed for a "lyxblogger" python module and, if present,
registered the "blog" format plus a "python -m lyxblogger" converter.
Nothing about this can work any more. LyXBlogger was last released in
June 2012 (0.42) and last touched upstream in May 2012; it was never on
PyPI. It is python 2 only: its modules use implicit relative imports
(from misc import pr3, import wordpresslib), which PEP 328 removed in
python 3, so importing it raises ModuleNotFoundError. Its installer
still uses distutils, dropped in python 3.12. Consequently checkModule()
already fails on any current system and the format was never registered
in the first place.
It also posted over WordPress XML-RPC, which current WordPress
installations disable in favour of the REST API.
Note this was also the only bare "python" left in a shipped converter
definition; every other one goes through ${python}. Repairing it that
way would only aim a python 2 module at python 3, replacing a silent
no-op with a guaranteed failure, so remove the format instead.
checkModule() has no caller left after this. It is kept as a generic
helper for probing python modules; removing it is a separate decision.
Assisted-by: Claude Opus 5
---
lib/RELEASE-NOTES | 5 +++++
lib/configure.py | 6 ------
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/lib/RELEASE-NOTES b/lib/RELEASE-NOTES
index 14b83d15f1..e208d69ae9 100644
--- a/lib/RELEASE-NOTES
+++ b/lib/RELEASE-NOTES
@@ -104,6 +104,11 @@
!!Changes with respect to external programs and libraries in 2.6:
+- Support for LyXBlogger (the "blog" export format) has been dropped.
+ LyXBlogger has been unmaintained since 2012, is python 2 only, and posts
+ over WordPress XML-RPC, which current WordPress installations disable by
+ default. It could not be used with the python 3 that LyX requires.
+
!!Known issues in version 2.6.0
diff --git a/lib/configure.py b/lib/configure.py
index fbbbca51ef..ae02d6641a 100644
--- a/lib/configure.py
+++ b/lib/configure.py
@@ -1033,12 +1033,6 @@ def checkConverterEntries():
addToRC(r'''\copier wordhtml "$${python} $$s/scripts/ext_copy.py $$i $$o"''')
- # Check if LyXBlogger is installed
- lyxblogger_found = checkModule('lyxblogger')
- if lyxblogger_found:
- addToRC(r'\Format blog blog "LyXBlogger" "" "" "" "document" ""')
- addToRC(r'\converter xhtml blog "python -m lyxblogger $$i" ""')
-
#
checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
rc_entry = [ r'\converter sxw latex "%%" ""' ])
--
2.39.5
>From 72191027c38af17686816b0c875ce71e0907a826 Mon Sep 17 00:00:00 2001
Date: Wed, 9 Sep 2026 10:34:58 +0200
Subject: [PATCH] Remove python 2 leftovers from the development scripts
None of this is reachable on python 3, so this is tidying rather than a
fix:
- "from __future__ import print_function" is a no-op on python 3 (six
scripts).
- unicodesymbols.py picked unichr() under "sys.version_info[0] < 3";
the branch is never taken and unichr() does not exist on python 3.
- cat.py picked the "rU" open mode under the same test. Besides being
dead, "U" was removed in python 3.11, so the branch could not work
even if it were reached. Python 3 enables universal newlines in text
mode anyway, which is what the comment there already said.
Assisted-by: Claude Opus 5
---
development/autotests/keytest.py | 1 -
development/cmake/doc/ReplaceValues.py | 1 -
development/cmake/po/cat.py | 15 +++------------
development/tools/generate_symbols_images.py | 1 -
development/tools/generate_symbols_list.py | 1 -
development/tools/unicodesymbols.py | 6 +-----
6 files changed, 4 insertions(+), 21 deletions(-)
diff --git a/development/autotests/keytest.py b/development/autotests/keytest.py
index ce3b245653..11c6b87d9b 100755
--- a/development/autotests/keytest.py
+++ b/development/autotests/keytest.py
@@ -8,7 +8,6 @@
# Adapted by Tommaso Cucinotta from the original MonKey Test by
# John McCabe-Dansted.
-from __future__ import print_function
import random
import os
import re
diff --git a/development/cmake/doc/ReplaceValues.py b/development/cmake/doc/ReplaceValues.py
index 18eaab3774..bf5d10efae 100755
--- a/development/cmake/doc/ReplaceValues.py
+++ b/development/cmake/doc/ReplaceValues.py
@@ -1,6 +1,5 @@
#! /usr/bin/python3
-from __future__ import print_function
# file ReplaceValues.py
#
diff --git a/development/cmake/po/cat.py b/development/cmake/po/cat.py
index 025f887716..6b9489b05c 100644
--- a/development/cmake/po/cat.py
+++ b/development/cmake/po/cat.py
@@ -1,6 +1,5 @@
#! /usr/bin/python3
-from __future__ import print_function
import sys
from getopt import getopt
@@ -29,17 +28,9 @@ if outfile:
out = open(outfile, "wb")
for f in args:
- if sys.version_info[0] < 3:
- # accept both windows and unix line endings, since it can
- # happen that we are on unix, but the file has been written on
- # windows or vice versa.
- mode = "rU"
- else:
- # The default behavior of Python 3 is to enable universal
- # newlines in text mode. Adding "U" gives a deprecation
- # warning.
- mode = "r"
- fil = open(f, mode)
+ # Python 3 enables universal newlines in text mode, so both windows
+ # and unix line endings are accepted.
+ fil = open(f, "r")
for l in fil:
# this does always write unix line endings since the file has
# been opened in binary mode. This is needed since both gettext
diff --git a/development/tools/generate_symbols_images.py b/development/tools/generate_symbols_images.py
index 0d06117ec2..463611aad5 100755
--- a/development/tools/generate_symbols_images.py
+++ b/development/tools/generate_symbols_images.py
@@ -19,7 +19,6 @@
# created images should never be replaced by automatically created ones.
-from __future__ import print_function
import os, re, string, sys, subprocess, tempfile, shutil
import Image
import io
diff --git a/development/tools/generate_symbols_list.py b/development/tools/generate_symbols_list.py
index ccdfe37454..0f8ec804e4 100755
--- a/development/tools/generate_symbols_list.py
+++ b/development/tools/generate_symbols_list.py
@@ -7,7 +7,6 @@
# Full author contact details are available in file CREDITS
-from __future__ import print_function
import sys,string,re,os,os.path
import io
diff --git a/development/tools/unicodesymbols.py b/development/tools/unicodesymbols.py
index d507f4a681..55898ad5d7 100755
--- a/development/tools/unicodesymbols.py
+++ b/development/tools/unicodesymbols.py
@@ -12,7 +12,6 @@
# This script reads a unicode symbol file and completes it in the given range
-from __future__ import print_function
import os, re, string, sys, unicodedata
import io
@@ -70,10 +69,7 @@ def complete(lines, start, stop):
l = l + 1
continue
if l >= len(lines) or lines[l][0] != i:
- if sys.version_info[0] < 3:
- c = unichr(i)
- else:
- c = chr(i)
+ c = chr(i)
name = unicodedata.name(c, "")
if name != "":
if unicodedata.combining(c):
--
2.39.5
--
lyx-devel mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-devel