b2f93424f3557b70152b9e0d0dc3b4f42afba569
[software/python-on-guile.git] / modules / language / python / module / io.scm
1 (define-module (language python module io)
2 #:use-module (language python module exceptions)
3 #:use-module ((language python module os)
4 #:select (get_blocking))
5 #:re-export (BlockingIOError)
6 #:export (UnsupportedOperation scm-port open DEFAULT_BUFFER_SIZE
7 IOBase RawIOBase BufferedIOBase FileIO
8 BytesIO BufferedReader BufferedWriter
9 BufferedRandom TextIOBase TextIOWrapper
10 StringIO))
11
12
13
14 (define (scm-port x)
15 (if (port? x)
16 x
17 (aif it (ref x '_port)
18 it
19 (raise ValueError "no port in scm-port"))))
20
21 (define-python-class UnsupportedOperation (OSError ValueError))
22
23 (define DEFAULT_BUFFER_SIZE 4096)
24
25 (define (path-it path)
26 (aif it (ref path '__fspath__)
27 (it)
28 path))
29
30 (def (open- path
31 (= mode "r")
32 (= buffering -1 )
33 (= encoding None)
34 (= errors None)
35 (= newline None)
36 (= closefd #t)
37 (= opener None))
38
39 (define modelist (string->list mode))
40 (define path (path-it path))
41 (define (clean ch l)
42 (filter (lambda (c) (not (eq? ch c))) l))
43 (let ((port (if (number? path)
44 (begin
45 (if (member #\a modelist)
46 (seek path 0 SEEK_END))
47 (if (member #\x modelist)
48 (error "cannot use mode 'x' for fd input"))
49 (cond
50 ((member #\r modelist)
51 (fdes->inport path))
52 ((member #\w modelist)
53 (fdes->outport path))))
54 (begin
55 (if (member #\x modelist)
56 (if (file-exists? path)
57 (raise OSError "mode='x' and file exists")
58 (set mode (list->string
59 (clean #\x modelist)))))
60 ((@ (guile) open-file) (path-it path) mode))))
61
62 (errors (if (bool errors)
63 (scm-str errors)
64 (let ((s (port-conversion-strategy port)))
65 (cond
66 ((eq? s 'error) "strict")
67 ((eq? s 'substitute) "replace")
68 ((eq? s 'escape) "basckslashreplace")))))
69
70 (encoding (if (eq? encoding None)
71 (port-encoding port)
72 encoding)))
73
74
75 ;; encoding
76 (set self 'encoding encoding)
77 (set-port-encoding! port encoding)
78
79 (case buffering
80 ((-1)
81 (setvbuf port 'block DEFAULT_BUFFER_SIZE))
82 ((0)
83 (setvbuf port 'none))
84 ((1)
85 (setvbuf port 'line))
86 (else
87 (setvbuf port 'block buffering)))
88
89 (cond
90 ((equal? errors "strict")
91 (set-port-conversion-strategy! port 'error))
92 ((equal? errors "replace")
93 (set-port-conversion-strategy! port 'substitute))
94 ((equal? errors "basckslashreplace")
95 (set-port-conversion-strategy! port 'escape))
96 (else
97 (set-port-conversion-strategy! port 'escape)))
98
99 port))
100
101
102 (def (open path
103 (= mode "r")
104 (= buffering -1 )
105 (= encoding None)
106 (= errors None)
107 (= newline None)
108 (= closefd #t)
109 (= opener None))
110
111 (let ((F
112 (FileIO (cons
113 (open path mode buffering encoding errors
114 newline closefd opener)
115 path)
116 mode)))
117 (if (member #\b (string->list mode))
118 F
119 (TextIOWrapper F encoding errors))))
120
121
122 ;;ABC
123
124 (define-syntax-rule (check self . l)
125 (aif it (ref self 'raw)
126 (let ((self it))
127 (if (ref self 'closed)
128 (raise ValueError "IO operation on closed port"))
129 . l)
130 (begin
131 (if (ref self 'closed)
132 (raise ValueError "IO operation on closed port"))
133 . l)))
134
135 (define-python-class IOBase ()
136 (define __init__
137 (lambda (self port)
138 (set self '_port port)
139 (set self 'closed (port-closed? port))))
140
141 (define __getport__
142 (lambda (self)
143 (check self
144 (ref self _port))))
145
146 (define close
147 (lambda (self)
148 (check self
149 (close-port (ref self '_port))
150 (set self 'closed #t))))
151
152 (define __enter__
153 (lambda (self)
154 (check self)
155 self))
156
157 (define __exit__
158 (lambda (self . x)
159 (check self
160 ((ref self 'close)))))
161
162 (define flush
163 (lambda (self)
164 (check self
165 (if ((ref self readable)) (drain-input (ref self '_port)))
166 (if ((ref self writeable)) (force-output (ref self '_port))))))
167
168 (define isatty
169 (lambda (self)
170 (check self
171 (isatty? (ref self '_port)))))
172
173 (define __iter__
174 (lambda (self)
175 (check self)
176 self))
177
178 (define __next__
179 (lambda (self)
180 (check self
181 (raise StopIteration))))
182
183 (define readable
184 (lambda (self)
185 (check self
186 (output-port? (ref self '_port)))))
187
188 (define readline
189 (lam (self (= size -1))
190 (check self
191 (raise UnsupportedOperation))))
192
193 (define readlines
194 (lam (self (= hint -1))
195 (check self
196 (raise UnsupportedOperation))))
197
198 (define seekable
199 (lambda (self)
200 (check self
201 (catch #t
202 (lambda () (seek (ref self '_port) 0 SEEK_CUR) #t)
203 (lambda x #f)))))
204
205 (define seek
206 (lambda* (self offset #:optional (whence SEEK_SET))
207 (check self
208 (if (not ((ref self seekable)))
209 (raise (ValueError "Not seekable")))
210 (seek (ref self '_port) offset whence))))
211
212
213 (define tell
214 (lambda (self)
215 (check self
216 (ftell (ref self '_port)))))
217
218 (define truncate
219 (lam (self (size None))
220 (check self
221 (if (eq? size None)
222 (truncate-file (ref self '_port))
223 (truncate-file (ref self '_port) size)))))
224
225
226 (define writable
227 (lambda (self)
228 (check self
229 (input-port? (ref self '_port)))))
230
231 (define writelines
232 (lambda (self lines)
233 (check self
234 (raise UnsupportedOperation))))
235
236 (define __del__
237 (lambda (self
238 ((ref self 'close))))))
239
240
241
242
243
244
245 (define-python-class RawIOBase (IOBase)
246 (define read
247 (lambda (self #:optional (size -1))
248 (check self
249 (bytes
250 (if (< size 0)
251 ((ref self 'readall))
252 (get-bytevector-n (ref self '_port) size))))))
253
254
255 (define readall
256 (lambda (self)
257 (check self
258 (bytes
259 (get-bytevector-all (ref self '_port))))))
260
261 (define readinto
262 (lambda (self b)
263 (check self
264 (let* ((n (len b))
265 (b (scm-bytevector b))
266 (m (get-bytevector-n! (ref self '_port) b 0 n)))
267 (if (eof? m)
268 (if (get_blocking port)
269 0
270 None))))))
271
272 (define write
273 (lambda (self b)
274 (check self
275 (let ((n (len b))
276 (b (scm-bytevector b)))
277 (put-bytevector (ref self '_port) b 0 n)
278 n)))))
279
280
281 (define-python-class BufferedIOBase (RawIOBase)
282 (define detach
283 (lambda (self)
284 (check self
285 (raise UnsupportedOperation "detach"))))
286
287 (define read1
288 (lambda* (self #:optional (size -1))
289 (check self
290 ((ref self 'read) size))))
291
292 (define readinto1
293 (lambda (self b)
294 (check self
295 ((ref self 'readinto) b)))))
296
297 (define-python-class FileIO (RawIOBase)
298 (define __init__
299 (lam (self name (= mode 'r') (= closefd #t) (= opener None))
300 (if (pair? name)
301 (set self '_port (car name))
302 (set self '_port
303 (open_ it
304 #:mode mode
305 #:closefd closefd
306 #:opener opener)))
307 (set self 'mode mode)
308 (set self 'name (cdr name)))))
309
310
311 (define-python-class BytesIO (BufferedIOBase)
312 (define __init__
313 (lambda* (self #:optional (initial_bytes None))
314 (if (eq? initial_bytes None)
315 (call-with-values open-bytevector-output-port
316 (lambda (port get-bytevector)
317 (set self '_port port)
318 (set self '_gtbv get-bytevector)))
319 (set self '_port
320 (open-bytevector-input-port initial_bytes)))))
321
322 (define getvalue
323 (lambda (self)
324 (check self
325 (bytes ((ref self '_gtbv)))))))
326
327 (define-python-class BufferedReader (BufferedIOBase)
328 (define __init__
329 (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE))
330 (let ((port (ref raw '_port)))
331 (case buffer_size
332 ((0)
333 (setvbuf port 'none))
334 ((1)
335 (setvbuf port 'line))
336 (else
337 (setvbuf port 'block buffer_size))))
338 (set self 'raw raw)))
339
340 (define peek
341 (lambda (self)
342 (raise UnsupportedOperation peek))))
343
344 (define-python-class BufferedWriter (BufferedIOBase)
345 (define __init__
346 (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE))
347 (let ((port (ref raw '_port)))
348 (case buffer_size
349 ((0)
350 (setvbuf port 'none))
351 ((1)
352 (setvbuf port 'line))
353 (else
354 (setvbuf port 'block buffer_size))))
355 (set self 'raw raw))))
356
357 (define-python-class BufferedRandom (BufferedIOBase)
358 (define __init__
359 (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE))
360 (let ((port (ref raw '_port)))
361 (case buffer_size
362 ((0)
363 (setvbuf port 'none))
364 ((1)
365 (setvbuf port 'line))
366 (else
367 (setvbuf port 'block buffer_size))))
368 (set self 'raw raw)))
369
370 (define peek
371 (lambda (self)
372 (raise UnsupportedOperation peek))))
373
374 (use-modules (ice-9 textual-ports))
375 (use-modules (ice-9 rdelim))
376
377 (define-python-class TextIOBase (IOBase)
378 (define read
379 (lambda (self size)
380 (check self
381 (let ((port (ref self '_port)))
382 (get-string-n port size)))))
383
384 (define readline
385 (lam (self (= size -1))
386 (check self
387 (let ((port (ref self '_port)))
388 (read-line port 'concat)))))
389
390 (define write
391 (lambda (self s)
392 (check self
393 (let ((port (ref self '_port)))
394 (put-string port (scm-str s) 0 (len s))
395 (len s))))))
396
397 (define (get-port x)
398 (aif it (ref x '_port)
399 it
400 (aif it (ref x 'raw)
401 (get-port it)
402 (raise (ValueError "No port associated to IO wrapper")))))
403
404 (define-python-class TextIOWrapper (TextIOBase)
405 (define __init__
406 (lam (self buffer
407 (= encoding None)
408 (= errors None)
409 (= newline None)
410 (= line_buffering #f)
411 (= write_through #f))
412 (set self 'raw buffer)
413 (let* ((port (get-port buffer))
414 (errors (if (bool errors)
415 (scm-str errors)
416 (let ((s (port-conversion-strategy port)))
417 (cond
418 ((eq? s 'error) "strict")
419 ((eq? s 'substitute) "replace")
420 ((eq? s 'escape) "basckslashreplace")))))
421 (encoding (if (eq? encoding None)
422 (port-encoding port)
423 encoding)))
424 ;; encoding
425 (set self 'encoding encoding)
426 (set-port-encoding! port encoding)
427
428 ;; buffering
429 (if line_buffering
430 (setvbuf port 'line))
431
432 (set self 'line_buffering line_buffering)
433
434 ;; errors
435 (set self 'error errors)
436 (cond
437 ((equal? errors "strict")
438 (set-port-conversion-strategy! port 'error))
439 ((equal? errors "replace")
440 (set-port-conversion-strategy! port 'substitute))
441 ((equal? errors "basckslashreplace")
442 (set-port-conversion-strategy! port 'escape))
443 (else
444 (set-port-conversion-strategy! port 'escape)))
445
446 ;; write trough
447 (set self 'write_trough write_trough)))))
448
449 (define-python-class StringIO (TextIOBase)
450 (define __init__
451 (lam (self (= initial_value "") (= newline "\n"))
452 (set self 'newline newline)
453 (if (equal? initial_value "")
454 (set self '_port (open-output-str))
455 (set self '_port (open-input-str initial_value)))))
456
457 (define getvalue
458 (lambda (self)
459 (check self
460 (get-output-string (ref self port))))))
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479 (define-python-class TextIOWrapper (TextIOBase))
480 (define-python-class StringIO (TextIOBase))
481