GeezerVison is showing github.com — Docs and docstrings for io.*.seek are inconsistent · Issue #107801 · python/cpython
Docs and docstrings for io.*.seek are inconsistent #107801DescriptionDocumentation
The documentation on text handles is misleading, in particular for the seek method.
Steps to reproduce
handle = open('/tmp/lines.txt')
help(handle.seek)
This produces the following documentation:
Help on built-in function seek:
seek(cookie, whence=0, /) method of _io.TextIOWrapper instance
Change stream position.
Change the stream position to the given byte offset. The offset is
interpreted relative to the position indicated by whence. Values
for whence are:
* 0 -- start of stream (the default); offset should be zero or positive
* 1 -- current stream position; offset may be negative
* 2 -- end of stream; offset is usually negative
Return the new absolute position.
Issues
- The documentation talks about a byte offset, but the interface mentions a cookie.
- The constants defined in
os module, like os.SEEK_END are not mentioned.
- whence=2 with negative offset fails.
Generally, the behaviour is very inconsistent, seek relative to the end fail, seek relative to the start works, but might yield a situation when read fails.
Full investigation of the issue
Linked PRs
Activitysunmy2019 commented on Aug 10, 2023 AA-Turner commented on Aug 12, 2023 @MatthiasWiesmann would you have time to write a PR with suggestied revisions of the help text?
To note, Python 3.12 will include the constants in os (see #104418), though there is quite a bit of inconsistency between the various seek() methods' docstrings:
|
/*[clinic input] |
|
_io._IOBase.seek |
|
cls: defining_class |
|
offset: int(unused=True) |
|
whence: int(unused=True, c_default='0') = os.SEEK_SET |
|
/ |
|
|
|
Change the stream position to the given byte offset. |
|
|
|
The offset is interpreted relative to the position indicated by whence. |
|
Values for whence are: |
|
|
|
* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive |
|
* os.SEEK_CUR or 1 -- current stream position; offset may be negative |
|
* os.SEEK_END or 2 -- end of stream; offset is usually negative |
|
|
|
Return the new absolute position. |
|
[clinic start generated code]*/ |
|
/*[clinic input] |
|
_io._Buffered.seek |
|
target as targetobj: object |
|
whence: int = 0 |
|
/ |
|
[clinic start generated code]*/ |
|
/*[clinic input] |
|
_io.BytesIO.seek |
|
pos: Py_ssize_t |
|
whence: int = 0 |
|
/ |
|
|
|
Change stream position. |
|
|
|
Seek to byte offset pos relative to position indicated by whence: |
|
0 Start of stream (the default). pos should be >= 0; |
|
1 Current position - pos may be negative; |
|
2 End of stream - pos usually negative. |
|
Returns the new absolute position. |
|
[clinic start generated code]*/ |
|
/*[clinic input] |
|
_io.FileIO.seek |
|
pos: object |
|
whence: int = 0 |
|
/ |
|
|
|
Move to new file position and return the file position. |
|
|
|
Argument offset is a byte count. Optional argument whence defaults to |
|
SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values |
|
are SEEK_CUR or 1 (move relative to current position, positive or negative), |
|
and SEEK_END or 2 (move relative to end of file, usually negative, although |
|
many platforms allow seeking beyond the end of a file). |
|
|
|
Note that not all file objects are seekable. |
|
[clinic start generated code]*/ |
|
/*[clinic input] |
|
_io.StringIO.seek |
|
pos: Py_ssize_t |
|
whence: int = 0 |
|
/ |
|
|
|
Change stream position. |
|
|
|
Seek to character offset pos relative to position indicated by whence: |
|
0 Start of stream (the default). pos should be >= 0; |
|
1 Current position - pos must be 0; |
|
2 End of stream - pos must be 0. |
|
Returns the new absolute position. |
|
[clinic start generated code]*/ |
|
/*[clinic input] |
|
_io.TextIOWrapper.seek |
|
cookie as cookieObj: object |
|
whence: int = 0 |
|
/ |
|
[clinic start generated code]*/ |
cc @erlend-aasland as the author of #104418 if you have any thoughts.
A Reacted by Erlend E. Aasland erlend-aasland commented on Aug 13, 2023 I agree that it is confusing that the parameter names for the low-level API seek() aren't used consistently throughout the docs/docstrings. Luckily, these are all positional-only, so we can easily normalise them without backwards compatibility concerns.
Suggested improvements:
- Consistently name the
seek() params
- Consistently use
io.SEEK_* constants instead of os.SEEK_*
- Document
io.SEEK_ constants
serhiy-storchaka commented on Aug 14, 2023
- The confusion is caused by the fact that
TextIOWrapper.seek() has no docstring, so pydoc shows a docstring from a parent class, which is inadequate due to significant difference in semantic.
- The first argument of
TextIOWrapper.seek() is not a byte offset, and not an offset at all. It is a "cookie", i.e. zero or an opaque number returned by tell(). Passing anything other than zero and the value returned by tell() when whence=SEEK_SET has undefined behavior.
- SEEK_CUR and SEEK_END only support zero as the first argument.
Neither forward seeking nor backward seeking are supported. The only supported are rewinding to the start and restoring the previous position saved by tell(). Confusion could be avoided if TextIOWrapper.tell() returned a special type instead of integer, but for now it would be a large breaking change (because the cookie can be saved in a file as an integer and used in other process). erlend-aasland commented on Aug 14, 2023 As a data point: on macOS, Linux, and FreeBSD, the lseek and fseek manpages uses offset and whence as parameter names. serhiy-storchaka commented on Aug 14, 2023 Because they only work with binary files. They do not work with TextIOWrapper which support multibyte stateful codecs. Reacted by Erlend E. Aasland erlend-aasland commented on Aug 14, 2023 There are a lot of tasks here. Suggesting to proceed in this order:
- Use whence instead of how as the name of the third position-only parameter of
os.lseek. Rationale: the SEEK_* constants are also used for this function. If the SEEK_* constants are to talk about the whence parameter, we should use it for os.lseek as well.
- Improve the documentation of
os.SEEK_SET, os.SEEK_CUR and os.SEEK_END; explain that they are used for the whence parameter of os.lseek and of the seek() method of file objects; add a very short description to each constant.
- Document
os.SEEK_HOLE and os.SEEK_DATA (exposed in os though posix)
- Consistently name
_io.*.seek() params where it makes sense.
- Improve the docs of the
seek() methods (Doc/library/io.rst)
- Improve the docstrings of the
seek() methods
hugovk commented on Nov 9, 2023 Lots of merged PRs here. 🚀
Anything still to do, or can the issue be closed? vadmium commented on Jul 26, 2024 Issue #82969 is open about IOBase.seek/tell vs TextIOWrapper and the offset/cookie parameter
Issue #122299 is about the byte vs character seek offsets picnixz commented on Dec 2, 2024 I'll close this issue as completed since #82969 was closed and #122299 is likely to be closed as well IMO. We can always add more docs if needed and use that issue as a tracker.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|