Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Expose preadv2 in os module
  • Loading branch information
pablogsal committed Jan 20, 2018
commit ea21128d44dc31bc1cd2f9c285dbcf8e3ad1fe18
13 changes: 13 additions & 0 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,19 @@ def test_pread(self):
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'pread'), "test needs posix.pread()")
def test_preadv2(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
try:
os.write(fd, b'test1tt2t3')
os.lseek(fd, 0, os.SEEK_SET)
buf = [bytearray(i) for i in [5, 3, 2]]
self.assertEqual(posix.preadv2(fd, buf, 0, os.RWF_SYNC), 10)
self.assertEqual([b'test1', b'tt2', b't3'], [bytes(i) for i in buf])
finally:
os.close(fd)


@unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()")
def test_pwrite(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
Expand Down
50 changes: 49 additions & 1 deletion Modules/clinic/posixmodule.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -3705,6 +3705,50 @@ os_pread(PyObject *module, PyObject *const *args, Py_ssize_t nargs)

#endif /* defined(HAVE_PREAD) */

#if defined(HAVE_PREAD)

PyDoc_STRVAR(os_preadv2__doc__,
"preadv2($module, fd, buffers, offset, flags, /)\n"
"--\n"
"\n"
"Read a number of bytes from a file descriptor starting at a particular offset.\n"
"\n"
"Read length bytes from file descriptor fd, starting at offset bytes from\n"
"the beginning of the file. The file offset remains unchanged.");

#define OS_PREADV2_METHODDEF \
{"preadv2", (PyCFunction)os_preadv2, METH_FASTCALL, os_preadv2__doc__},

static Py_ssize_t
os_preadv2_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
int flags);

static PyObject *
os_preadv2(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int fd;
PyObject *buffers;
Py_off_t offset;
int flags;
Py_ssize_t _return_value;

if (!_PyArg_ParseStack(args, nargs, "iOO&i:preadv2",
&fd, &buffers, Py_off_t_converter, &offset, &flags)) {
goto exit;
}
_return_value = os_preadv2_impl(module, fd, buffers, offset, flags);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyLong_FromSsize_t(_return_value);

exit:
return return_value;
}

#endif /* defined(HAVE_PREAD) */

PyDoc_STRVAR(os_write__doc__,
"write($module, fd, data, /)\n"
"--\n"
Expand Down Expand Up @@ -6239,6 +6283,10 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#define OS_PREAD_METHODDEF
#endif /* !defined(OS_PREAD_METHODDEF) */

#ifndef OS_PREADV2_METHODDEF
#define OS_PREADV2_METHODDEF
#endif /* !defined(OS_PREADV2_METHODDEF) */

#ifndef OS_PIPE_METHODDEF
#define OS_PIPE_METHODDEF
#endif /* !defined(OS_PIPE_METHODDEF) */
Expand Down Expand Up @@ -6410,4 +6458,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=6345053cd5992caf input=a9049054013a1b77]*/
/*[clinic end generated code: output=2b1fdec599a15ea9 input=a9049054013a1b77]*/
66 changes: 66 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -8163,6 +8163,64 @@ os_pread_impl(PyObject *module, int fd, int length, Py_off_t offset)
}
#endif /* HAVE_PREAD */

#ifdef HAVE_PREAD
/*[clinic input]
os.preadv2 -> Py_ssize_t

fd: int
buffers: object
offset: Py_off_t
flags: int
/

Read a number of bytes from a file descriptor starting at a particular offset.

Read length bytes from file descriptor fd, starting at offset bytes from
the beginning of the file. The file offset remains unchanged.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wait... is this description correct? it seems to be copied from two other functions?

[clinic start generated code]*/

static Py_ssize_t
os_preadv2_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
int flags)
/*[clinic end generated code: output=fd040c20cf504631 input=f4a633d90005eb65]*/
{
Py_ssize_t cnt, n;
int async_err = 0;
struct iovec *iov;
Py_buffer *buf;

if (!PySequence_Check(buffers)) {
PyErr_SetString(PyExc_TypeError,
"preadv2() arg 2 must be a sequence");
return -1;
}

cnt = PySequence_Size(buffers);
if (cnt < 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please always add { ... } to if blocks: see PEP 7. Same comment for the whole PR.

return -1;

if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0)
return -1;

do {
Py_BEGIN_ALLOW_THREADS
n = preadv2(fd, iov, cnt, offset, flags);
Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));

iov_cleanup(iov, buf, cnt);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we exit early (e.g. due to flags != 0), then this cleanup function doesn't get called. Is that OK?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I have corrected it in 4ddbca9. I could not use easily a goto label to have a clean exit section due to the check for async error in the signal handler so I just clean before exiting if flags != 0.

if (n < 0) {
if (!async_err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PEP 7 coding style: add { ... }

posix_error();
return -1;
}

return n;
}


#endif /* HAVE PREADV2 */


/*[clinic input]
os.write -> Py_ssize_t
Expand Down Expand Up @@ -12506,6 +12564,7 @@ static PyMethodDef posix_methods[] = {
OS_READ_METHODDEF
OS_READV_METHODDEF
OS_PREAD_METHODDEF
OS_PREADV2_METHODDEF
OS_WRITE_METHODDEF
OS_WRITEV_METHODDEF
OS_PWRITE_METHODDEF
Expand Down Expand Up @@ -12953,6 +13012,13 @@ all_ins(PyObject *m)
if (PyModule_AddIntMacro(m, F_TEST)) return -1;
#endif

#ifdef HAVE_READV
if (PyModule_AddIntConstant(m, "RWF_DSYNC", RWF_DSYNC)) return -1;
if (PyModule_AddIntConstant(m, "RWF_HIPRI", RWF_HIPRI)) return -1;
if (PyModule_AddIntConstant(m, "RWF_SYNC", RWF_SYNC)) return -1;
if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1;
#endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These constants were added at different times, so they should be checked for individually, like:

#ifdef RWF_DSYNC
    if (PyModule_AddIntConstant(m, "RWF_DSYNC", RWF_DSYNC)) return -1;
#endif
#ifdef RWF_HIPRI
    if (PyModule_AddIntConstant(m, "RWF_HIPRI", RWF_HIPRI)) return -1;
#endif
...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also EOPNOTSUPP flag might be important to add?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's already available as errno.EOPNOTSUPP.


#ifdef HAVE_SPAWNV
if (PyModule_AddIntConstant(m, "P_WAIT", _P_WAIT)) return -1;
if (PyModule_AddIntConstant(m, "P_NOWAIT", _P_NOWAIT)) return -1;
Expand Down