1 (define-module (language python module io
)
2 #:use-module
(oop pf-objects
)
3 #:use-module
(system foreign
)
4 #:use-module
(ice-9 binary-ports
)
5 #:use-module
(ice-9 textual-ports
)
6 #:use-module
(ice-9 rdelim
)
8 #:use-module
(language python exceptions
)
9 #:use-module
(language python def
)
10 #:use-module
(language python try
)
11 #:use-module
(language python string
)
12 #:use-module
(language python list
)
13 #:use-module
(language python bytes
)
14 #:use-module
(language python bool
)
16 #:use-module
(language python module errno
)
17 #:re-export
(BlockingIOError)
19 #:export
(UnsupportedOperation scm-port DEFAULT_BUFFER_SIZE
20 IOBase RawIOBase BufferedIOBase FileIO
21 BytesIO BufferedReader BufferedWriter
22 BufferedRandom TextIOBase TextIOWrapper
25 (define-syntax-rule (aif it p x y
) (let ((it p
)) (if it x y
)))
27 (define-syntax-rule (ca code
)
30 (lambda x
(raise error x
))))
32 (define-syntax-rule (rm code
)
35 (raise error
(errno) ((@ (guile) strerror
) (errno)))
40 (define fcntl2
(pointer->procedure int
41 (dynamic-func "fcntl" (dynamic-link))
43 (define fcntl3
(pointer->procedure int
44 (dynamic-func "fcntl" (dynamic-link))
47 (define (get_blocking fd
)
48 (let ((fd (if (port? fd
) (port->fdes fd
) fd
)))
49 (if (= (logand O_NONBLOCK
(rm (fcntl2 fd F_GETFL
))) 0)
56 (aif it
(ref x
'_port
)
60 (raise ValueError
"no port in scm-port")))))
62 (define-python-class UnsupportedOperation
(OSError ValueError
))
64 (define DEFAULT_BUFFER_SIZE
4096)
66 (define (path-it path
)
70 (aif it
(ref path
'__fspath__
)
83 (define modelist
(string->list mode
))
84 (define path
* (path-it path
))
86 (filter (lambda (c) (not (eq? ch c
))) l
))
88 (let* ((port (if (number? path
)
90 (if (member #\a modelist
)
91 (seek path
* 0 SEEK_END
))
92 (if (member #\x modelist
)
93 (error "cannot use mode 'x' for fd input"))
95 ((member #\r modelist
)
97 ((member #\w modelist
)
98 (fdes->outport path
*))))
100 (if (member #\x modelist
)
101 (if (file-exists? path
*)
102 (raise OSError
"mode='x' and file exists")
103 (set mode
(list->string
104 (clean #\x modelist
)))))
105 ((@ (guile) open-file
) path
* mode
))))
107 (errors (if (bool errors
)
109 (let ((s (port-conversion-strategy port
)))
111 ((eq? s
'error
) "strict")
112 ((eq? s
'substitute
) "replace")
113 ((eq? s
'escape
) "basckslashreplace")))))
115 (encoding (if (eq? encoding None
)
121 (set-port-encoding! port encoding
)
125 (setvbuf port
'block DEFAULT_BUFFER_SIZE
))
127 (setvbuf port
'none
))
129 (setvbuf port
'line
))
131 (setvbuf port
'block buffering
)))
134 ((equal? errors
"strict")
135 (set-port-conversion-strategy! port
'error
))
136 ((equal? errors
"replace")
137 (set-port-conversion-strategy! port
'substitute
))
138 ((equal? errors
"basckslashreplace")
139 (set-port-conversion-strategy! port
'escape
))
141 (set-port-conversion-strategy! port
'escape
)))
157 (open- path mode buffering encoding errors
158 newline closefd opener
)
161 (if (member #\b (string->list mode
))
163 (TextIOWrapper F encoding errors
))))
169 (check (self a b
) . l
))
172 (check (self port b
) . l
))
174 ((_ (self port it
) . l
)
176 (aif it2
(ref it
'raw
)
179 (if (ref self
'closed
)
180 (raise ValueError
"IO operation on closed port"))
181 (let ((port (ref it
'_port
)))
187 (define-python-class IOBase
()
190 (set self
'_port port
)
191 (set self
'closed
(port-closed? port
))))
200 (check (self port it
)
202 (set it
'closed
#t
))))
212 ((ref self
'close
)))))
217 (if ((ref self
'readable
)) (drain-input port
))
218 (if ((ref self
'writeable
)) (force-output port
)))))
233 (raise StopIteration
))))
238 (output-port? port
))))
241 (lam (self (= size -
1))
243 (raise UnsupportedOperation
))))
246 (lam (self (= hint -
1))
248 (raise UnsupportedOperation
))))
254 (lambda () (seek port
0 SEEK_CUR
) #t
)
258 (lambda* (self offset
#:optional
(whence SEEK_SET
))
260 (if (not ((ref self seekable
)))
261 (raise (ValueError "Not seekable")))
262 (seek port offset whence
))))
271 (lam (self (= size None
))
275 (truncate-file port size
)))))
281 (input-port? port
))))
286 (raise UnsupportedOperation
))))
291 (aif it2
(ref it
'raw
)
293 (let* ((port (ref it
'_port
))
294 (cln (ref self
'__name__
))
295 (nm (port-filename port
))
296 (mod (port-mode port
)))
297 (format #f
"~a ~a : ~a" cln nm mod
))))))
301 ((ref self
'close
)))))
305 (define (wrap x
) (if (eof-object? x
) #vu8
() x
))
306 (define (wraps x
) (if (eof-object? x
) "" x
))
309 (define-python-class RawIOBase
(IOBase)
312 (let ((x (read self
1)))
318 (lam (self (= size -
1))
322 ((ref self
'readall
))
323 (wrap (get-bytevector-n port size
)))))))
330 (wrap (get-bytevector-all port
))))))
336 (b (scm-bytevector b
))
337 (m (get-bytevector-n! port b
0 n
)))
338 (if (eq? m eof-object
)
339 (if (get_blocking (ref self
'_port
))
348 (b (scm-bytevector b
)))
349 (put-bytevector port b
0 n
)
353 (define-python-class BufferedIOBase
(RawIOBase)
357 (raise UnsupportedOperation
"detach"))))
360 (lambda* (self #:optional
(size -
1))
362 ((ref self
'read
) size
))))
367 ((ref self
'readinto
) b
)))))
369 (define-python-class FileIO
(RawIOBase)
371 (lam (self name
(= mode
'r
') (= closefd
#t
) (= opener None
))
373 (set self
'_port
(car name
))
375 (open- (path-it name
)
379 (set self
'mode mode
)
380 (set self
'name
(cdr name
)))))
383 (define-python-class BytesIO
(BufferedIOBase)
385 (lambda* (self #:optional
(initial_bytes None
))
386 (if (eq? initial_bytes None
)
387 (call-with-values open-bytevector-output-port
388 (lambda (port get-bytevector
)
389 (set self
'_port port
)
390 (set self
'_gtbv get-bytevector
)))
392 (open-bytevector-input-port
393 (scm-bytevector initial_bytes
))))))
398 (bytes ((ref self
'_gtbv
)))))))
400 (define-python-class BufferedReader
(BufferedIOBase)
402 (lambda* (self raw
#:optional
(buffer_size DEFAULT_BUFFER_SIZE
))
403 (let ((port (ref raw
'_port
)))
406 (setvbuf port
'none
))
408 (setvbuf port
'line
))
410 (setvbuf port
'block buffer_size
))))
411 (set self
'raw raw
)))
415 (raise UnsupportedOperation peek
))))
417 (define-python-class BufferedWriter
(BufferedIOBase)
419 (lambda* (self raw
#:optional
(buffer_size DEFAULT_BUFFER_SIZE
))
420 (let ((port (ref raw
'_port
)))
423 (setvbuf port
'none
))
425 (setvbuf port
'line
))
427 (setvbuf port
'block buffer_size
))))
428 (set self
'raw raw
))))
430 (define-python-class BufferedRandom
(BufferedIOBase)
432 (lambda* (self raw
#:optional
(buffer_size DEFAULT_BUFFER_SIZE
))
433 (let ((port (ref raw
'_port
)))
436 (setvbuf port
'none
))
438 (setvbuf port
'line
))
440 (setvbuf port
'block buffer_size
))))
441 (set self
'raw raw
)))
445 (raise UnsupportedOperation peek
))))
447 (define-python-class TextIOBase
(IOBase)
450 (let ((x ((ref self
'read
) 1)))
456 (lam (self (= size -
1))
459 (wraps (get-string-all port
))
460 (wraps (get-string-n port size
))))))
463 (lam (self (= size -
1))
465 (wraps (read-line port
'concat
)))))
470 (put-string port
(scm-str s
) 0 (len s
))
474 (aif it
(ref x
'_port
)
478 (raise (ValueError "No port associated to IO wrapper")))))
480 (define-python-class TextIOWrapper
(TextIOBase)
486 (= line_buffering
#f
)
487 (= write_through
#f
))
488 (set self
'raw buffer
)
489 (let* ((port (get-port buffer
))
490 (errors (if (bool errors
)
492 (let ((s (port-conversion-strategy port
)))
494 ((eq? s
'error
) "strict")
495 ((eq? s
'substitute
) "replace")
496 ((eq? s
'escape
) "basckslashreplace")))))
497 (encoding (if (eq? encoding None
)
501 (set self
'encoding encoding
)
502 (set-port-encoding! port encoding
)
506 (setvbuf port
'line
))
508 (set self
'line_buffering line_buffering
)
511 (set self
'error errors
)
513 ((equal? errors
"strict")
514 (set-port-conversion-strategy! port
'error
))
515 ((equal? errors
"replace")
516 (set-port-conversion-strategy! port
'substitute
))
517 ((equal? errors
"basckslashreplace")
518 (set-port-conversion-strategy! port
'escape
))
520 (set-port-conversion-strategy! port
'escape
)))
523 (set self
'write_through write_through
)))))
525 (define-python-class StringIO
(TextIOBase)
527 (lam (self (= initial_value
"") (= newline
"\n"))
528 (set self
'newline newline
)
529 (if (equal? initial_value
"")
530 (set self
'_port
(open-output-string))
531 (set self
'_port
(open-input-string initial_value
)))))
536 (get-output-string port
)))))