Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-41818: Add termios.tcgetwinsize(), termios.tcsetwinsize(). Update docs. #23686

Merged
merged 9 commits into from
Aug 27, 2021
15 changes: 15 additions & 0 deletions Doc/library/termios.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ The module defines the following functions:
output, :const:`TCIOFF` to suspend input, or :const:`TCION` to restart input.


.. function:: tcgetwinsize(fd)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one follow up PR as I forgot to fix it up here, these need versionadded tags. I'll take care of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpshead Thank you for making the improvements!


Return a list ``[ws_row, ws_col]`` containing the tty window size for file
descriptor *fd*. Requires :const:`termios.TIOCGWINSZ` or
:const:`termios.TIOCGSIZE`.


.. function:: tcsetwinsize(fd, winsize)

Set the tty window size for file descriptor *fd* from *winsize*, which is
a list like the one returned by :func:`tcgetwinsize`. Requires at least
one of the pairs (:const:`termios.TIOCGWINSZ`, :const:`termios.TIOCSWINSZ`);
(:const:`termios.TIOCGSIZE`, :const:`termios.TIOCSSIZE`) to be defined.


.. seealso::

Module :mod:`tty`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Soumendra Ganguly: add termios.tcgetwinsize(), termios.tcsetwinsize().
66 changes: 65 additions & 1 deletion Modules/clinic/termios.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

143 changes: 143 additions & 0 deletions Modules/termios.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,141 @@ termios_tcflow_impl(PyObject *module, int fd, int action)
Py_RETURN_NONE;
}

/*[clinic input]
termios.tcgetwinsize

fd: fildes
/

Get the tty winsize for file descriptor fd.

Returns a list [ws_row, ws_col].
[clinic start generated code]*/

static PyObject *
termios_tcgetwinsize_impl(PyObject *module, int fd)
/*[clinic end generated code: output=31825977d5325fb6 input=c7ed8aa957d108c0]*/
{
#if defined(TIOCGWINSZ)
gpshead marked this conversation as resolved.
Show resolved Hide resolved
termiosmodulestate *state = PyModule_GetState(module);
struct winsize w;
if (ioctl(fd, TIOCGWINSZ, &w) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

PyObject *v;
if (!(v = PyList_New(2))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When returning a constant number of values, it is more typical to use a tuple than a list. PyTuple_New and equivalent item setting calls. also update the docstrings in the comment above and on the next function to mention "two item tuple" and use ().

Copy link
Contributor Author

@8vasu 8vasu Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpshead Sir, thank you for the review. I should mention that I used PyList_New() instead of PyTuple_New() because I wanted this function to resemble the other low level function termios.tcgetattr(), which also returns a list. Do you confirm this change?

Added: I should have been more specific: termios.tcgetattr() also returns a list of fixed length, and that cannot (?) be changed (backward compatibility?). I was wondering if termios.tcgetwinsize() should also behave similarly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up making the changes!

return NULL;
}

PyList_SetItem(v, 0, PyLong_FromLong((long)w.ws_row));
PyList_SetItem(v, 1, PyLong_FromLong((long)w.ws_col));
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
#elif defined(TIOCGSIZE)
termiosmodulestate *state = PyModule_GetState(module);
struct ttysize s;
if (ioctl(fd, TIOCGSIZE, &s) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

PyObject *v;
if (!(v = PyList_New(2))) {
return NULL;
}

PyList_SetItem(v, 0, PyLong_FromLong((long)s.ts_lines));
PyList_SetItem(v, 1, PyLong_FromLong((long)s.ts_cols));
if (PyErr_Occurred()) {
Py_DECREF(v);
return NULL;
}
return v;
#else
PyErr_SetString(PyExc_NotImplementedError,
"requires termios.TIOCGWINSZ and/or termios.TIOCGSIZE");
return NULL;
#endif /* defined(TIOCGWINSZ) */
}

/*[clinic input]
termios.tcsetwinsize

fd: fildes
winsize as winsz: object
/

Set the tty winsize for file descriptor fd.

The winsize to be set is taken from the winsize argument, which
is a list like the one returned by tcgetwinsize().
gpshead marked this conversation as resolved.
Show resolved Hide resolved
[clinic start generated code]*/

static PyObject *
termios_tcsetwinsize_impl(PyObject *module, int fd, PyObject *winsz)
/*[clinic end generated code: output=2ac3c9bb6eda83e1 input=c495180b2b932a30]*/
{
#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
if (!PyList_Check(winsz) || PyList_Size(winsz) != 2) {
PyErr_SetString(PyExc_TypeError,
"tcsetwinsize, arg 2: must be 2 element list");
return NULL;
}

termiosmodulestate *state = PyModule_GetState(module);
struct winsize w;
/* Get the old winsize because it might have
more fields such as xpixel, ypixel. */
if (ioctl(fd, TIOCGWINSZ, &w) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

w.ws_row = (unsigned short) PyLong_AsLong(PyList_GetItem(winsz, 0));
w.ws_col = (unsigned short) PyLong_AsLong(PyList_GetItem(winsz, 1));
if (PyErr_Occurred()) {
return NULL;
}

if (ioctl(fd, TIOCSWINSZ, &w) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

Py_RETURN_NONE;
#elif defined(TIOCGSIZE) && defined(TIOCSSIZE)
if (!PyList_Check(winsz) || PyList_Size(winsz) != 2) {
PyErr_SetString(PyExc_TypeError,
"tcsetwinsize, arg 2: must be 2 element list");
return NULL;
}

termiosmodulestate *state = PyModule_GetState(module);
struct ttysize s;
/* Get the old ttysize because it might have more fields. */
if (ioctl(fd, TIOCGSIZE, &s) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

s.ts_lines = (unsigned short) PyLong_AsLong(PyList_GetItem(winsz, 0));
s.ts_cols = (unsigned short) PyLong_AsLong(PyList_GetItem(winsz, 1));
if (PyErr_Occurred()) {
return NULL;
}

if (ioctl(fd, TIOCSSIZE, &s) == -1) {
return PyErr_SetFromErrno(state->TermiosError);
}

Py_RETURN_NONE;
#else
PyErr_SetString(PyExc_NotImplementedError,
"requires termios.TIOCGWINSZ, termios.TIOCSWINSZ and/or termios.TIOCGSIZE, termios.TIOCSSIZE");
return NULL;
#endif /* defined(TIOCGWINSZ) && defined(TIOCSWINSZ) */
}

static PyMethodDef termios_methods[] =
{
TERMIOS_TCGETATTR_METHODDEF
Expand All @@ -323,6 +458,8 @@ static PyMethodDef termios_methods[] =
TERMIOS_TCDRAIN_METHODDEF
TERMIOS_TCFLUSH_METHODDEF
TERMIOS_TCFLOW_METHODDEF
TERMIOS_TCGETWINSIZE_METHODDEF
TERMIOS_TCSETWINSIZE_METHODDEF
{NULL, NULL}
};

Expand Down Expand Up @@ -841,6 +978,9 @@ static struct constant {
#ifdef TIOCGSERIAL
{"TIOCGSERIAL", TIOCGSERIAL},
#endif
#ifdef TIOCGSIZE
{"TIOCGSIZE", TIOCGSIZE},
#endif
#ifdef TIOCGSOFTCAR
{"TIOCGSOFTCAR", TIOCGSOFTCAR},
#endif
Expand Down Expand Up @@ -973,6 +1113,9 @@ static struct constant {
#ifdef TIOCSSERIAL
{"TIOCSSERIAL", TIOCSSERIAL},
#endif
#ifdef TIOCSSIZE
{"TIOCSSIZE", TIOCSSIZE},
#endif
#ifdef TIOCSSOFTCAR
{"TIOCSSOFTCAR", TIOCSSOFTCAR},
#endif
Expand Down