aboutsummaryrefslogtreecommitdiff
path: root/discord/file.py
diff options
context:
space:
mode:
authorRapptz <[email protected]>2019-03-18 07:54:36 -0400
committerRapptz <[email protected]>2019-03-18 07:54:36 -0400
commit5e65ec978cf278fefcc5586e2df732bc7a8bed4e (patch)
tree83ad80c9e486a83e725b39d1ad2b3a6e9f48a19b /discord/file.py
parentMinor typo fix. (diff)
downloaddiscord.py-5e65ec978cf278fefcc5586e2df732bc7a8bed4e.tar.xz
discord.py-5e65ec978cf278fefcc5586e2df732bc7a8bed4e.zip
Take back ownership of files from aiohttp for retrying requests.
Fix #1809
Diffstat (limited to 'discord/file.py')
-rw-r--r--discord/file.py45
1 files changed, 35 insertions, 10 deletions
diff --git a/discord/file.py b/discord/file.py
index d3a775fd..7240b884 100644
--- a/discord/file.py
+++ b/discord/file.py
@@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE.
"""
import os.path
+import io
class File:
"""A parameter object used for :meth:`abc.Messageable.send`
@@ -52,11 +53,28 @@ class File:
Whether the attachment is a spoiler.
"""
- __slots__ = ('fp', 'filename', '_true_fp')
+ __slots__ = ('fp', 'filename', '_original_pos', '_owner', '_closer')
def __init__(self, fp, filename=None, *, spoiler=False):
self.fp = fp
- self._true_fp = None
+
+ if isinstance(fp, io.IOBase):
+ if not (fp.seekable() and fp.readable()):
+ raise ValueError('File buffer {!r} must be seekable and readable'.format(fp))
+ self.fp = fp
+ self._original_pos = fp.tell()
+ self._owner = False
+ else:
+ self.fp = open(fp, 'rb')
+ self._original_pos = 0
+ self._owner = True
+
+ # aiohttp only uses two methods from IOBase
+ # read and close, since I want to control when the files
+ # close, I need to stub it so it doesn't close unless
+ # I tell it to
+ self._closer = self.fp.close
+ self.fp.close = lambda: None
if filename is None:
if isinstance(fp, str):
@@ -66,15 +84,22 @@ class File:
else:
self.filename = filename
- if spoiler and not self.filename.startswith('SPOILER_'):
+ if spoiler and self.filename is not None and not self.filename.startswith('SPOILER_'):
self.filename = 'SPOILER_' + self.filename
- def open_file(self):
- fp = self.fp
- if isinstance(fp, str):
- self._true_fp = fp = open(fp, 'rb')
- return fp
+ def reset(self, *, seek=True):
+ # The `seek` parameter is needed because
+ # the retry-loop is iterated over multiple times
+ # starting from 0, as an implementation quirk
+ # the resetting must be done at the beginning
+ # before a request is done, since the first index
+ # is 0, and thus false, then this prevents an
+ # unnecessary seek since it's the first request
+ # done.
+ if seek:
+ self.fp.seek(self._original_pos)
def close(self):
- if self._true_fp:
- self._true_fp.close()
+ self.fp.close = self._closer
+ if self._owner:
+ self._closer()