Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Doc/library/readline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,38 @@ Readline library in general.

.. versionadded:: 3.13

.. data:: READLINE_VERSION_INFO

A named tuple containing the two components of the Readline library
version that was used for building the module: *major* and *minor*.
Both values are integers.
The components can also be accessed by name,
so ``readline.READLINE_VERSION_INFO[0]`` is equivalent to
``readline.READLINE_VERSION_INFO.major`` and so on.
This may be different from the Readline library actually used at runtime,
which is available as :const:`readline_version_info`.

With the ``editline`` backend, this is the version of the Readline
interface emulated by libedit, not the version of libedit.

.. versionadded:: next

.. data:: readline_version_info

A named tuple containing the version of the Readline library
actually loaded by the interpreter,
with the same fields as :const:`READLINE_VERSION_INFO`.

.. versionadded:: next

.. data:: readline_version

The version string of the Readline library actually loaded by the
interpreter, like ``'8.3'``.
With the ``editline`` backend, this is ``'EditLine wrapper'``.

.. versionadded:: next

Init file
---------

Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,16 @@ pydoc
(Contributed by Serhiy Storchaka in :gh:`153906`.)


readline
--------

* Added constants :const:`~readline.READLINE_VERSION_INFO`,
:const:`~readline.readline_version_info` and
:const:`~readline.readline_version`, which provide information about
the version of the Readline library in use.
(Contributed by Serhiy Storchaka in :gh:`157485`.)


re
--

Expand Down
24 changes: 5 additions & 19 deletions Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,27 +427,13 @@ def collect_readline(info_add):
except ImportError:
return

def format_attr(attr, value):
if isinstance(value, int):
return "%#x" % value
else:
return value

attributes = (
"_READLINE_VERSION",
"_READLINE_RUNTIME_VERSION",
"_READLINE_LIBRARY_VERSION",
"backend",
"READLINE_VERSION_INFO",
"readline_version_info",
"readline_version",
)
copy_attributes(info_add, readline, 'readline.%s', attributes,
formatter=format_attr)

if not hasattr(readline, "_READLINE_LIBRARY_VERSION"):
# _READLINE_LIBRARY_VERSION has been added to CPython 3.7
doc = getattr(readline, '__doc__', '')
if 'libedit readline' in doc:
info_add('readline.library', 'libedit readline')
elif 'GNU readline' in doc:
info_add('readline.library', 'GNU readline')
copy_attributes(info_add, readline, 'readline.%s', attributes)


def run_command(cmd, check=True, **kwargs):
Expand Down
48 changes: 35 additions & 13 deletions Lib/test/test_readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,14 @@
# Skip tests if there is no readline module
readline = import_module('readline')

if hasattr(readline, "_READLINE_LIBRARY_VERSION"):
is_editline = ("EditLine wrapper" in readline._READLINE_LIBRARY_VERSION)
else:
is_editline = readline.backend == "editline"
is_editline = readline.backend == "editline"


def setUpModule():
if verbose:
# Python implementations other than CPython may not have
# these private attributes
if hasattr(readline, "_READLINE_VERSION"):
print(f"readline version: {readline._READLINE_VERSION:#x}")
print(f"readline runtime version: {readline._READLINE_RUNTIME_VERSION:#x}")
if hasattr(readline, "_READLINE_LIBRARY_VERSION"):
print(f"readline library version: {readline._READLINE_LIBRARY_VERSION!r}")
print(f"readline version: {readline.READLINE_VERSION_INFO}")
print(f"readline runtime version: {readline.readline_version_info}")
print(f"readline library version: {readline.readline_version!r}")
print(f"use libedit emulation? {is_editline}")


Expand Down Expand Up @@ -210,7 +203,36 @@ def test_append_limited_history(self):

class TestReadline(unittest.TestCase):

@unittest.skipIf(readline._READLINE_VERSION < 0x0601 and not is_editline,
def _test_readline_version(self, v):
self.assertIsInstance(v[:], tuple)
self.assertEqual(len(v), 2)
self.assertIsInstance(v[0], int)
self.assertIsInstance(v[1], int)
self.assertIsInstance(v.major, int)
self.assertIsInstance(v.minor, int)
self.assertEqual(v[0], v.major)
self.assertEqual(v[1], v.minor)
self.assertGreaterEqual(v.major, 4)
self.assertGreaterEqual(v.minor, 0)

def test_readline_version(self):
self._test_readline_version(readline.READLINE_VERSION_INFO)
self._test_readline_version(readline.readline_version_info)
self.assertIsInstance(readline.readline_version, str)
if not is_editline:
self.assertEqual(readline.readline_version,
'%d.%d' % readline.readline_version_info)
# Private names kept for backward compatibility.
self.assertEqual(readline._READLINE_VERSION,
readline.READLINE_VERSION_INFO.major << 8 |
readline.READLINE_VERSION_INFO.minor)
self.assertEqual(readline._READLINE_RUNTIME_VERSION,
readline.readline_version_info.major << 8 |
readline.readline_version_info.minor)
self.assertIs(readline._READLINE_LIBRARY_VERSION,
readline.readline_version)

@unittest.skipIf(readline.READLINE_VERSION_INFO < (6, 1) and not is_editline,
"not supported in this library version")
def test_init(self):
# Issue #19884: Ensure that the ANSI sequence "\033[1034h" is not
Expand Down Expand Up @@ -366,7 +388,7 @@ def display(substitution, matches, longest_match_length):
# See https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES
# - editline: history size is broken on OS X 10.11.6.
# Newer versions were not tested yet.
@unittest.skipIf(readline._READLINE_VERSION < 0x600,
@unittest.skipIf(readline.READLINE_VERSION_INFO < (6, 0),
"this readline version does not support history-size")
@unittest.skipIf(is_editline,
"editline history size configuration is broken")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add constants :const:`readline.READLINE_VERSION_INFO`,
:const:`readline.readline_version_info` and :const:`readline.readline_version`,
which provide information about the version of the Readline library in use.
102 changes: 91 additions & 11 deletions Modules/readline.c
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,96 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
}


PyDoc_STRVAR(readline_version_info__doc__,
"readline.readline_version_info\n\
\n\
Readline version information as a named tuple.");

static PyStructSequence_Field readline_version_info_fields[] = {
{"major", "Major release number"},
{"minor", "Minor release number"},
{0}
};

static PyStructSequence_Desc readline_version_info_desc = {
"readline.readline_version_info", /* name */
readline_version_info__doc__, /* doc */
readline_version_info_fields, /* fields */
2
};

static PyObject *
make_readline_version_info(PyTypeObject *type, int number)
{
PyObject *version;
int pos = 0;
int major = (number >> 8) & 0xff;
int minor = number & 0xff;

version = PyStructSequence_New(type);
if (version == NULL) {
return NULL;
}

#define SetItem(VALUE) \
PyStructSequence_SET_ITEM(version, pos++, VALUE); \
if (PyErr_Occurred()) { \
Py_DECREF(version); \
return NULL; \
}

SetItem(PyLong_FromLong(major))
SetItem(PyLong_FromLong(minor))
#undef SetItem

return version;
}

static int
add_version_constants(PyObject *m)
{
if (PyModule_AddIntConstant(m, "_READLINE_VERSION",
RL_READLINE_VERSION) < 0) {
return -1;
}
if (PyModule_AddIntConstant(m, "_READLINE_RUNTIME_VERSION",
rl_readline_version) < 0) {
return -1;
}
PyObject *obj = PyUnicode_FromString(rl_library_version);
if (obj == NULL) {
return -1;
}
if (PyModule_AddObjectRef(m, "readline_version", obj) < 0 ||
PyModule_AddObjectRef(m, "_READLINE_LIBRARY_VERSION", obj) < 0)
{
Py_DECREF(obj);
return -1;
}
Py_DECREF(obj);
PyTypeObject *version_type;
version_type = PyStructSequence_NewType(&readline_version_info_desc);
if (version_type == NULL) {
return -1;
}
if (PyModule_Add(m, "READLINE_VERSION_INFO",
make_readline_version_info(version_type,
RL_READLINE_VERSION)) < 0)
{
Py_DECREF(version_type);
return -1;
}
if (PyModule_Add(m, "readline_version_info",
make_readline_version_info(version_type,
rl_readline_version)) < 0)
{
Py_DECREF(version_type);
return -1;
}
Py_DECREF(version_type);
return 0;
}

/* Initialize the module */

PyDoc_STRVAR(doc_module,
Expand Down Expand Up @@ -1853,17 +1943,7 @@ PyInit_readline(void)
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif

if (PyModule_AddIntConstant(m, "_READLINE_VERSION",
RL_READLINE_VERSION) < 0) {
goto error;
}
if (PyModule_AddIntConstant(m, "_READLINE_RUNTIME_VERSION",
rl_readline_version) < 0) {
goto error;
}
if (PyModule_AddStringConstant(m, "_READLINE_LIBRARY_VERSION",
rl_library_version) < 0)
{
if (add_version_constants(m) < 0) {
goto error;
}

Expand Down
Loading