You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use io.IOBase as base class for StreamingBody #879
StreamingBody is a "file-like object", but file-like objects should probably inherit from IOBase or implement a similar suite of methods. This will provide an easy fix for #767.
Are there any methods in particular that you are looking for? It may be difficult to inherit from IOBase as not all of its methods may be possible to implement (given that we are reading from an unseekable stream from the server underneath).
@kyleknap, IOBase can certainly work with streaming data. This is sufficient for my current use-case, but with a little more work I think it could be fully supported. My main interest was in line-by-line iteration:
classStreamingBodyIO(RawIOBase):
"""Wrap a boto StreamingBody in the IOBase API."""def__init__(self, body):
self.body=bodydefreadable(self):
returnTruedefread(self, n=-1):
n=Noneifn<0elsenreturnself.body.read(n)
Reacted by Dean Gurvitz, Michael Selik and Mike McDermott
There are two other other open feature requests (#1197 and #767), which should be solved by inheriting from IOBase or RawIOBase, and one open PR (#1195) which should be closed because it does not use this method.
...actually, is there any point to keeping StreamingBody around at all? It looks like urllib3 implements content length checking itself, and it looks like that socket timeout thing might've been resolved as well.
When returning a file-like object, such as StreamingBody, io.IOBase is a very reasonable abstract to assume. From the io.IOBase docstring:
This class provides empty abstract implementations for many methods that derived classes can override selectively; the default implementations represent a file that cannot be read, written or seeked.
E.g., the missing io.IOBase attributes make it very dificult to lazily read a compressed file from S3:
Another problem that this should resolve: text response bodies that one wishes to deal with as such, but still stream. TextIOWrapper(StreamingBody(...), encoding="utf-8") seems like it should work, but instead raises the exception: builtins.AttributeError: 'StreamingBody' object has no attribute 'readable'
Reacted by Scott Leonard, SwagMuffinMcYoloPants, Paul Cowan, Kevin, Zidaan Dutta, Eric Wong and Mike McDermott
We've taken a couple stabs at this but both attempts have failed (#2208, #2150) once we do more extensive integration testing across our packages (botocore, boto3, aws-cli, s3transfer).
This class currently defines a close method that will close the underlying stream that the StreamingBody class is wrapping. When we extend from io.IOBase we inherit the finalization behavior, which causes close to be called as part of finalization. In the lifecycle of returning a request from botocore we end up wrapping the underlying stream with StreamingBody twice. This has the implication that we see the full life cycle of one of these objects that can be GC'd before we return the response and end up returning a StreamingBody where the underlying stream has already been closed and can no longer be read.
This behavior seems to be avoidable on Python 3 by stubbing out the implementation of __del__, but on Python 2 it doesn't seem like we can avoid this. Unfortunately, I think we're blocked on inheriting this as long as we support Python 2 and even for Python 3 I have mixed feelings on the robustness of this. I think we'll have to take another look at this in the future, otherwise we might just have to re-implement the methods directly onto StreamingBody.
@joguSD Could you perhaps implement the finalization behavior yourself, and lie about inheriting from IOBase using an if TYPE_CHECKING or something similar?
@glyph There are likely some more intricate approaches we can take to circumvent the finalization behavior in Python 2. Our initial research suggested it was going to be a fairly deep dive to get this working nicely across all of our packages.
Given we're ending Python 2 support in the next 5 months, I'd say it's more likely we'll implement the Python 3 __del__ solution then, and keep the code simpler if possible.
@nateprewitt The approach we took for this sort of thing in Twisted when we had areas where 2 was holding us back was we'd implement the 3-only version of the thing but not expose it for py2, so py3 users could start taking advantage. It wouldn't be a regression for py2 users if you did it today, after all.
In April I submitted a PR for a short-term fix for this. The PR has not been merged, or closed, or commented on.
Can anyone here tell me the status of this repo? I see fresh commits, so it's actively maintained.
How can I get the PR merged, or get feedback on why it can't be merged.
Should I raise a support request with AWS in the console? (I wouldn't even know which service to select.)
Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.
Activity
kyleknap commentedon Apr 15, 2016
Are there any methods in particular that you are looking for? It may be difficult to inherit from IOBase as not all of its methods may be possible to implement (given that we are reading from an unseekable stream from the server underneath).
akvadrako commentedon Sep 8, 2016
@kyleknap, IOBase can certainly work with streaming data. This is sufficient for my current use-case, but with a little more work I think it could be fully supported. My main interest was in line-by-line iteration:
atrigent commentedon May 29, 2017
There are two other other open feature requests (#1197 and #767), which should be solved by inheriting from
IOBaseorRawIOBase, and one open PR (#1195) which should be closed because it does not use this method.atrigent commentedon May 29, 2017
...actually, is there any point to keeping
StreamingBodyaround at all? It looks like urllib3 implements content length checking itself, and it looks like that socket timeout thing might've been resolved as well.GFernie commentedon Oct 9, 2017
When returning a file-like object, such as StreamingBody, io.IOBase is a very reasonable abstract to assume. From the io.IOBase docstring:
E.g., the missing io.IOBase attributes make it very dificult to lazily read a compressed file from S3:
Here's an accepted cpython PR for the same issue:
python/cpython#3249
glyph commentedon Aug 30, 2018
Another problem that this should resolve: text response bodies that one wishes to deal with as such, but still stream.
TextIOWrapper(StreamingBody(...), encoding="utf-8")seems like it should work, but instead raises the exception:builtins.AttributeError: 'StreamingBody' object has no attribute 'readable'AlJohri commentedon Nov 30, 2018
@GFernie did this already get fixed? I was able to get the following code working:
it lazily reads from a compressed json.gz file
glyph commentedon Dec 27, 2018
I am also very curious about @AlJohri's question! Is this actually fixed now?
evmin commentedon Nov 4, 2019
Would be keen to get it fixed as well.
evmin commentedon Nov 4, 2019
Especially since there has been a pull request.
joguSD commentedon Nov 10, 2020
We've taken a couple stabs at this but both attempts have failed (#2208, #2150) once we do more extensive integration testing across our packages (botocore, boto3, aws-cli, s3transfer).
This class currently defines a
closemethod that will close the underlying stream that theStreamingBodyclass is wrapping. When we extend fromio.IOBasewe inherit the finalization behavior, which causescloseto be called as part of finalization. In the lifecycle of returning a request frombotocorewe end up wrapping the underlying stream withStreamingBodytwice. This has the implication that we see the full life cycle of one of these objects that can be GC'd before we return the response and end up returning aStreamingBodywhere the underlying stream has already been closed and can no longer be read.This behavior seems to be avoidable on Python 3 by stubbing out the implementation of
__del__, but on Python 2 it doesn't seem like we can avoid this. Unfortunately, I think we're blocked on inheriting this as long as we support Python 2 and even for Python 3 I have mixed feelings on the robustness of this. I think we'll have to take another look at this in the future, otherwise we might just have to re-implement the methods directly ontoStreamingBody.glyph commentedon Jan 27, 2021
@joguSD Could you perhaps implement the finalization behavior yourself, and lie about inheriting from
IOBaseusing anif TYPE_CHECKINGor something similar?nateprewitt commentedon Jan 27, 2021
@glyph There are likely some more intricate approaches we can take to circumvent the finalization behavior in Python 2. Our initial research suggested it was going to be a fairly deep dive to get this working nicely across all of our packages.
Given we're ending Python 2 support in the next 5 months, I'd say it's more likely we'll implement the Python 3
__del__solution then, and keep the code simpler if possible.glyph commentedon Jan 27, 2021
@nateprewitt The approach we took for this sort of thing in Twisted when we had areas where 2 was holding us back was we'd implement the 3-only version of the thing but not expose it for py2, so py3 users could start taking advantage. It wouldn't be a regression for py2 users if you did it today, after all.
mdavis-xyz commentedon Sep 21, 2021
In April I submitted a PR for a short-term fix for this. The PR has not been merged, or closed, or commented on.
Can anyone here tell me the status of this repo? I see fresh commits, so it's actively maintained.
How can I get the PR merged, or get feedback on why it can't be merged.
Should I raise a support request with AWS in the console? (I wouldn't even know which service to select.)
tim-finnigan commentedon Feb 23, 2022
Closing this as the PR linked above was merged.
github-actions commentedon Feb 23, 2022
Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.