1 | # Copyright (C) 2014, Stefan Schwarzer <sschwarzer@sschwarzer.net> |
---|
2 | # and ftputil contributors (see `doc/contributors.txt`) |
---|
3 | # See the file LICENSE for licensing terms. |
---|
4 | |
---|
5 | """ |
---|
6 | See docstring of class `BufferedIO`. |
---|
7 | """ |
---|
8 | |
---|
9 | import io |
---|
10 | |
---|
11 | |
---|
12 | __all__ = ["BufferedIOAdapter"] |
---|
13 | |
---|
14 | |
---|
15 | class BufferedIOAdapter(io.BufferedIOBase): |
---|
16 | """ |
---|
17 | Adapt a file object returned from `socket.makefile` to the |
---|
18 | interfaces of `io.BufferedReader` or `io.BufferedWriter`, so that |
---|
19 | the new object can be wrapped by `io.TextIOWrapper`. |
---|
20 | |
---|
21 | This is only needed with Python 2, since in Python 3 |
---|
22 | `socket.makefile` already returns a `BufferedReader` or |
---|
23 | `BufferedWriter` object (depending on mode). |
---|
24 | """ |
---|
25 | |
---|
26 | def __init__(self, fobj, is_readable=False, is_writable=False): |
---|
27 | # Don't call baseclass constructor for this adapter. |
---|
28 | # pylint: disable=super-init-not-called |
---|
29 | # |
---|
30 | # This is the return value of `socket.makefile` and is already |
---|
31 | # buffered. |
---|
32 | self.raw = fobj |
---|
33 | self._is_readable = is_readable |
---|
34 | self._is_writable = is_writable |
---|
35 | |
---|
36 | @property |
---|
37 | def closed(self): |
---|
38 | # pylint: disable=missing-docstring |
---|
39 | return self.raw.closed |
---|
40 | |
---|
41 | def close(self): |
---|
42 | self.raw.close() |
---|
43 | |
---|
44 | def fileno(self): |
---|
45 | return self.raw.fileno() |
---|
46 | |
---|
47 | def isatty(self): |
---|
48 | # It's highly unlikely that this file is interactive. |
---|
49 | return False |
---|
50 | |
---|
51 | def seekable(self): |
---|
52 | return False |
---|
53 | |
---|
54 | # |
---|
55 | # Interface for `BufferedReader` |
---|
56 | # |
---|
57 | def readable(self): |
---|
58 | return self._is_readable |
---|
59 | |
---|
60 | def read(self, *arg): |
---|
61 | return self.raw.read(*arg) |
---|
62 | |
---|
63 | read1 = read |
---|
64 | |
---|
65 | def readline(self, *arg): |
---|
66 | return self.raw.readline(*arg) |
---|
67 | |
---|
68 | def readlines(self, *arg): |
---|
69 | return self.raw.readlines(*arg) |
---|
70 | |
---|
71 | def readinto(self, bytearray_): |
---|
72 | data = self.raw.read(len(bytearray_)) |
---|
73 | bytearray_[:len(data)] = data |
---|
74 | return len(data) |
---|
75 | |
---|
76 | # |
---|
77 | # Interface for `BufferedWriter` |
---|
78 | # |
---|
79 | def writable(self): |
---|
80 | return self._is_writable |
---|
81 | |
---|
82 | def flush(self): |
---|
83 | self.raw.flush() |
---|
84 | |
---|
85 | # Derived from `socket.py` in Python 2.6 and 2.7. |
---|
86 | # There doesn't seem to be a public API for this. |
---|
87 | def _write_buffer_size(self): |
---|
88 | """Return current size of the write buffer in bytes.""" |
---|
89 | # pylint: disable=protected-access |
---|
90 | if hasattr(self.raw, "_wbuf_len"): |
---|
91 | # Python 2.6.3 - 2.7.5 |
---|
92 | return self.raw._wbuf_len |
---|
93 | elif hasattr(self.raw, "_get_wbuf_len"): |
---|
94 | # Python 2.6 - 2.6.2. (Strictly speaking, all other |
---|
95 | # Python 2.6 versions have a `_get_wbuf_len` method, but |
---|
96 | # for 2.6.3 and up it returns `_wbuf_len`). |
---|
97 | return self.raw._get_wbuf_len() |
---|
98 | else: |
---|
99 | # Fallback. In the context of `write` this means the file |
---|
100 | # appears to be unbuffered. |
---|
101 | return 0 |
---|
102 | |
---|
103 | def write(self, bytes_or_bytearray): |
---|
104 | # `BufferedWriter.write` has to return the number of written |
---|
105 | # bytes, but files returned from `socket.makefile` in Python 2 |
---|
106 | # return `None`. Hence provide a workaround. |
---|
107 | old_buffer_byte_count = self._write_buffer_size() |
---|
108 | added_byte_count = len(bytes_or_bytearray) |
---|
109 | self.raw.write(bytes_or_bytearray) |
---|
110 | new_buffer_byte_count = self._write_buffer_size() |
---|
111 | return (old_buffer_byte_count + added_byte_count - |
---|
112 | new_buffer_byte_count) |
---|
113 | |
---|
114 | def writelines(self, lines): |
---|
115 | self.raw.writelines(lines) |
---|