Thanks to visit codestin.com
Credit goes to github.com

Skip to content

make io.pyi import everything from _io.py(i), like io.py does #1395

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

Merged
merged 10 commits into from
Jun 22, 2017
Merged

Conversation

matthiaskramm
Copy link
Contributor

No description provided.

Also, as far as possible, try to simplify, by moving methods into base
classes.
@matthiaskramm matthiaskramm changed the title WIP: make io.pyi import everything from _io.py(i), like io.py does make io.pyi import everything from _io.py(i), like io.py does Jun 10, 2017
@matthiaskramm
Copy link
Contributor Author

This PR got larger than expected. The basic motivation is to allow code like

io.BufferedReader(fp)

to pass type-checking. (Which it didn't before, since io didn't have a BufferedReader)

@gvanrossum
Copy link
Member

Do you have code that directly imports _io? If not, maybe we should get rid of it completely, like we did in the PY3 stubs.

Anyway, I tested this without our internal code and it found one issue, io.BufferedIOBase is supposed to define __enter__ and __exit__.

I am also still worried about some classes not inheriting from BinaryIO where they should. BinaryIO is abstract, we must ensure the abstractness or concreteness of various classes matches those in the stdlib.

@matthiaskramm
Copy link
Contributor Author

Do you have code that directly imports _io?

We do, unfortunately. Some in internal code, but I'm also seeing occurrences in third party packages like pymock and astroid.

I am also still worried about some classes not inheriting from BinaryIO where they should. BinaryIO is abstract, we must ensure the abstractness or concreteness of various classes matches those in the stdlib.

Right. Made _IOBase inherit from BinaryIO. (Note that _TextIOBase inherits from _IOBase, too, but is a TextIO. So you have a class that inherits both from BinaryIO and TextIO, which is a bit awkward. But that's also how it was modelled before this PR.)

@gvanrossum
Copy link
Member

Seeing all the # type: ignore lines I wonder if it wouldn't be better to ignore the runtime/implementation inheritance between _TextIOBase and _IOBase and just claim (to the type system) that _TextIOBase doesn't inherit from _IOBase? Or perhaps construct a new fictitious class/type that represents the common methods like seek()?

@matthiaskramm
Copy link
Contributor Author

I don't see why not. Let me look into it.

Copy link
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

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

Thanks for all the cleanup! I left some comments from comparing this with the C code and (mostly) the docs.

stdlib/2/_io.pyi Outdated
def seek(self, offset: int, whence: int = ...) -> int: ...
def seekable(self) -> bool: ...
def tell(self) -> int: ...
def truncate(self, size: Optional[int] = ...) -> int: ...
def writable(self) -> bool: ...
def write(self, s: bytes) -> int: ...
def __exit__(self, type, value, traceback) -> bool: ...
# This just returns self:
Copy link
Member

Choose a reason for hiding this comment

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

Then why not use a self type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

stdlib/2/_io.pyi Outdated
def seek(self, offset: int, whence: int = ...) -> int: ...
def seekable(self) -> bool: ...
def tell(self) -> int: ...
def truncate(self, size: Optional[int] = ...) -> int: ...
def writable(self) -> bool: ...
def write(self, s: bytes) -> int: ...
def __exit__(self, type, value, traceback) -> bool: ...
Copy link
Member

Choose a reason for hiding this comment

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

Why did you remove the argument types here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My bad. The types got under the wheel when I shifted __exit__ back and fro.

That said, I'm not sure what we get out of having types on __exit__. This function is invoked by the type-checker, not user code (nobody ever does a manual x.__exit__(a, b,c)), and the parameters are not originating from user code, either.

Either way, I added types.

stdlib/2/_io.pyi Outdated
def detach(self) -> "_BufferedIOBase": ...
def __enter__(self) -> '_BufferedIOBase': ...
Copy link
Member

Choose a reason for hiding this comment

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

Looks odd to have double quotes on one line and single on the next. Since this is a stub, we can just remove all quotes anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

stdlib/2/_io.pyi Outdated

class FileIO(_RawIOBase):
class FileIO(_RawIOBase, BytesIO): # type: ignore # for __enter__
Copy link
Member

Choose a reason for hiding this comment

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

Would this not be necessary if you used self types for these __enter__ methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

stdlib/2/io.pyi Outdated
@@ -7,98 +7,28 @@
from typing import List, BinaryIO, TextIO, IO, overload, Iterator, Iterable, Any, Union, Optional
import _io

DEFAULT_BUFFER_SIZE = 0
from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation,
Copy link
Member

Choose a reason for hiding this comment

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

These need x as x imports to make clear that they're re-exported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

stdlib/2/_io.pyi Outdated

class BufferedRWPair(_BufferedIOBase):
def peek(self, n: int = ...) -> str: ...
def __init__(self, reader: _RawIOBase, writer: _RawIOBase) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

This also takes buffer_size and max_buffer_size arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

stdlib/2/_io.pyi Outdated
class _TextIOBase(_IOBase):

# Note: In the actual _io.py, _TextIOBase inherits from _IOBase.
class _TextIOBase(TextIO):
errors = ... # type: Optional[str]
newlines = ... # type: Union[str, unicode]
Copy link
Member

Choose a reason for hiding this comment

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

According to the docs it can also be None or a tuple of strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, on _TextIOBase, this always returns None:

https://github.com/python/cpython/blob/2.7/Modules/_io/textio.c#L104

overloaded below.

stdlib/2/_io.pyi Outdated
def _checkSeekable(self) -> None: ...
def _checkWritable(self) -> None: ...
def close(self) -> None: ...
def detach(self) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

This returns the underlying BufferedIOBase, not None.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to make this IO, since _TextIOBase doesn't inherit from BufferedIOBase, and the underlying stream might be either.
buffer is IO below, too.

stdlib/2/_io.pyi Outdated
_CHUNK_SIZE = ... # type: int

def open(file: Union[int, str], mode: str = ...) -> _IOBase: ...
def __init__(self, buffer: IO, encoding: unicode = ...,
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be IO[bytes].

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this works:

_io.TextIOWrapper(_io.StringIO(u"hello world")).read()

stdlib/2/_io.pyi Outdated

def open(file: Union[int, str], mode: str = ...) -> _IOBase: ...
def __init__(self, buffer: IO, encoding: unicode = ...,
errors: unicode = ..., newline: unicode = ...,
Copy link
Member

Choose a reason for hiding this comment

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

Further up you had encoding and errors attributes as str on _TextIOBase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. These parameters are actually more permissive. They allow None, as well.

@matthiaskramm
Copy link
Contributor Author

Thanks for the thorough review!

@matthiaskramm
Copy link
Contributor Author

Is there a way to tell github that (you think) you addressed all the requested changes on a PR? It seems we occasionally have dangling PRs because it's not clear whether the author is still working on it or not.

@gvanrossum
Copy link
Member

gvanrossum commented Jun 13, 2017 via email

@JelleZijlstra
Copy link
Member

That's also something I've missed in GitHub's workflow (similar to "request review" in Phabricator).

I'll take another look at this PR in a few days.

@gvanrossum
Copy link
Member

Well, "request review" exists -- it's just that "request re-review" doesn't. :-)

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

I personally think this is good to go, but I'll leave it up to @JelleZijlstra to approve and merge.

@JelleZijlstra JelleZijlstra merged commit c4e0580 into master Jun 22, 2017
@JelleZijlstra JelleZijlstra deleted the io branch June 22, 2017 03:04
@JelleZijlstra
Copy link
Member

I guess I was stretching the definition of "a few days" :)

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants