blob: b9ec50dfbb04d9ed3c39addc6893c78246b027ac [file] [log] [blame]
Guido van Rossum28524c72007-02-27 05:47:44 +00001# Copyright 2006 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""New I/O library.
5
6See PEP XXX; for now: http://docs.google.com/Doc?id=dfksfvqd_1cn5g5m
7"""
8
9__author__ = "Guido van Rossum <guido@python.org>"
10
11__all__ = ["open", "RawIOBase", "FileIO", "SocketIO", "BytesIO"]
12
13import os
14
15def open(filename, mode="r", buffering=None, *, encoding=None):
16 """Replacement for the built-in open function, with encoding parameter."""
17 assert isinstance(filename, str)
18 assert isinstance(mode, str)
19 assert buffering is None or isinstance(buffering, int)
20 assert encoding is None or isinstance(encoding, str)
21 modes = set(mode)
22 if modes - set("arwb+t") or len(mode) > len(modes):
23 raise ValueError("invalid mode: %r" % mode)
24 reading = "r" in modes
25 writing = "w" in modes or "a" in modes
26 binary = "b" in modes
27 appending = "a" in modes
28 updating = "+" in modes
29 text = "t" in modes or not binary
30 if text and binary:
31 raise ValueError("can't have text and binary mode at once")
32 if reading + writing + appending > 1:
33 raise ValueError("can't have read/write/append mode at once")
34 if not (reading or writing or appending):
35 raise ValueError("must have exactly one of read/write/append mode")
36 if binary and encoding is not None:
37 raise ValueError("binary mode doesn't take an encoding")
38 raw = FileIO(filename,
39 (reading and "r" or "") +
40 (writing and "w" or "") +
41 (appending and "a" or "") +
42 (updating and "+" or ""))
43 if buffering is None:
44 buffering = 8*1024 # International standard buffer size
45 if buffering < 0:
46 raise ValueError("invalid buffering size")
47 if buffering == 0:
48 if binary:
49 return raw
50 raise ValueError("can't have unbuffered text I/O")
51 if updating:
52 buffer = BufferedRandom(raw, buffering)
53 elif writing:
54 buffer = BufferedWriter(raw, buffering)
55 else:
56 assert reading
57 buffer = BufferedReader(raw, buffering)
58 if binary:
59 return buffer
60 assert text
61 textio = TextIOWrapper(buffer) # Universal newlines default to on
62 return textio
63
64
65class RawIOBase:
66
67 """Base class for raw binary I/O."""
68
69 def read(self, n):
70 b = bytes(n.__index__())
71 self.readinto(b)
72 return b
73
74 def readinto(self, b):
75 raise IOError(".readinto() not supported")
76
77 def write(self, b):
78 raise IOError(".write() not supported")
79
80 def seek(self, pos, whence=0):
81 raise IOError(".seek() not supported")
82
83 def tell(self):
84 raise IOError(".tell() not supported")
85
86 def truncate(self, pos=None):
87 raise IOError(".truncate() not supported")
88
89 def close(self):
90 pass
91
92 def seekable(self):
93 return False
94
95 def readable(self):
96 return False
97
98 def writable(self):
99 return False
100
101 def __enter__(self):
102 return self
103
104 def __exit__(self, *args):
105 self.close()
106
107 def fileno(self):
108 raise IOError(".fileno() not supported")
109
110
111class FileIO(RawIOBase):
112
113 """Raw I/O implementation for OS files."""
114
115 def __init__(self, filename, mode):
116 self._seekable = None
117 self._mode = mode
118 if mode == "r":
119 flags = os.O_RDONLY
120 elif mode == "w":
121 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
122 self._writable = True
123 elif mode == "r+":
124 flags = os.O_RDWR
125 else:
126 assert 0, "unsupported mode %r (for now)" % mode
127 if hasattr(os, "O_BINARY"):
128 flags |= os.O_BINARY
129 self._fd = os.open(filename, flags)
130
131 def readinto(self, b):
132 # XXX We really should have os.readinto()
133 b[:] = os.read(self._fd, len(b))
134 return len(b)
135
136 def write(self, b):
137 return os.write(self._fd, b)
138
139 def seek(self, pos, whence=0):
140 os.lseek(self._fd, pos, whence)
141
142 def tell(self):
143 return os.lseek(self._fd, 0, 1)
144
145 def truncate(self, pos=None):
146 if pos is None:
147 pos = self.tell()
148 os.ftruncate(self._fd, pos)
149
150 def close(self):
151 os.close(self._fd)
152
153 def readable(self):
154 return "r" in self._mode or "+" in self._mode
155
156 def writable(self):
157 return "w" in self._mode or "+" in self._mode or "a" in self._mode
158
159 def seekable(self):
160 if self._seekable is None:
161 try:
162 os.lseek(self._fd, 0, 1)
163 except os.error:
164 self._seekable = False
165 else:
166 self._seekable = True
167 return self._seekable
168
Neal Norwitz8b41c3d2007-02-27 06:26:14 +0000169 # XXX(nnorwitz): is there any reason to redefine __enter__ & __exit__?
170 # Both already have the same impl in the base class.
Guido van Rossum28524c72007-02-27 05:47:44 +0000171 def __enter__(self):
172 return self
173
174 def __exit__(self, *args):
175 self.close()
176
177 def fileno(self):
178 return self._fd
179
180
181class SocketIO(RawIOBase):
182
183 """Raw I/O implementation for stream sockets."""
184
185 def __init__(self, sock, mode):
186 assert mode in ("r", "w", "rw")
187 self._sock = sock
188 self._mode = mode
189 self._readable = "r" in mode
190 self._writable = "w" in mode
191 self._seekable = False
192
193 def readinto(self, b):
194 return self._sock.recv_into(b)
195
196 def write(self, b):
197 return self._sock.send(b)
198
199 def close(self):
200 self._sock.close()
201
202 def readable(self):
203 return "r" in self._mode
204
205 def writable(self):
206 return "w" in self._mode
207
Neal Norwitz8b41c3d2007-02-27 06:26:14 +0000208 # XXX(nnorwitz)??? def fileno(self): return self._sock.fileno()
209
Guido van Rossum28524c72007-02-27 05:47:44 +0000210
211class BytesIO(RawIOBase):
212
213 """Raw I/O implementation for bytes, like StringIO."""
214
215 def __init__(self, inital_bytes=None):
216 self._buffer = b""
217 self._pos = 0
218 if inital_bytes is not None:
219 self._buffer += inital_bytes
220
221 def getvalue(self):
222 return self._buffer
223
224 def read(self, n):
225 assert n >= 0
226 newpos = min(len(self._buffer), self._pos + n)
227 b = self._buffer[self._pos : newpos]
228 self._pos = newpos
229 return b
230
231 def readinto(self, b):
232 b[:] = self.read(len(b))
233
234 def write(self, b):
235 n = len(b)
236 newpos = self._pos + n
237 self._buffer[self._pos:newpos] = b
238 self._pos = newpos
239 return n
240
241 def seek(self, pos, whence=0):
242 if whence == 0:
243 self._pos = max(0, pos)
244 elif whence == 1:
245 self._pos = max(0, self._pos + pos)
246 elif whence == 2:
247 self._pos = max(0, len(self._buffer) + pos)
248 else:
249 raise IOError("invalid whence value")
250
251 def tell(self):
252 return self._pos
253
254 def truncate(self, pos=None):
255 if pos is None:
256 pos = self._pos
257 else:
258 self._pos = max(0, pos)
259 del self._buffer[pos:]
260
261 def readable(self):
262 return True
263
264 def writable(self):
265 return True
266
267 def seekable(self):
268 return True